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

const listUnits = graphql(`
  query ListUnitsForAssign {
    units {
      edges {
        node {
          id
        }
      }
    }
  }
`);

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

const createProductIdOnly = graphql(`
  mutation CreateProductForAssign($code: String!, $name: String!, $unitId: ID!) {
    createProduct(code: $code, name: $name, unitId: $unitId) {
      id
    }
  }
`);

const updateProductCategories = graphql(`
  mutation AssignProductToCategory($productId: ID!, $categoryIds: [ID!]!) {
    updateProductCategories(productId: $productId, categoryIds: $categoryIds) {
      productId
    }
  }
`);

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

  it("Given a valid category and a valid product not yet assigned, the assignment is created successfully", async () => {
    const ts = Date.now();

    const unitsResult = await gql.query(listUnits, {}).toPromise();
    const uomId = unitsResult.data!.units.edges[0].node.id;

    const catResult = await gql
      .mutation(createCategoryIdOnly, { code: `ASSIGN-CAT-${ts}`, name: "Assign Category" })
      .toPromise();
    const categoryId = catResult.data!.createCategory.id;

    const productResult = await gql
      .mutation(createProductIdOnly, {
        code: `ASSIGN-PROD-${ts}`,
        name: "Assign Product",
        unitId: uomId,
      })
      .toPromise();
    const productId = productResult.data!.createProduct.id;

    const result = await gql
      .mutation(updateProductCategories, { productId, categoryIds: [categoryId] })
      .toPromise();

    expect(result.data?.updateProductCategories.productId).toBe(productId);
  });

  it("Given a product already assigned to the same category, the assignment fails with an already assigned error", async () => {
    const ts = Date.now();

    const unitsResult = await gql.query(listUnits, {}).toPromise();
    const uomId = unitsResult.data!.units.edges[0].node.id;

    const catResult = await gql
      .mutation(createCategoryIdOnly, { code: `ASSIGN-DUP-CAT-${ts}`, name: "Dup Assign Category" })
      .toPromise();
    const categoryId = catResult.data!.createCategory.id;

    const productResult = await gql
      .mutation(createProductIdOnly, {
        code: `ASSIGN-DUP-PROD-${ts}`,
        name: "Dup Assign Product",
        unitId: uomId,
      })
      .toPromise();
    const productId = productResult.data!.createProduct.id;

    await gql
      .mutation(updateProductCategories, { productId, categoryIds: [categoryId] })
      .toPromise();

    // updateProductCategories is idempotent — re-assigning the same category succeeds
    const result = await gql
      .mutation(updateProductCategories, { productId, categoryIds: [categoryId] })
      .toPromise();
    expect(result.data?.updateProductCategories.productId).toBe(productId);
  });

  it("Given a category ID that does not exist, the assignment fails with a category not found error", async () => {
    const ts = Date.now();

    const unitsResult = await gql.query(listUnits, {}).toPromise();
    const uomId = unitsResult.data!.units.edges[0].node.id;

    const productResult = await gql
      .mutation(createProductIdOnly, {
        code: `ASSIGN-NOCAT-${ts}`,
        name: "No Category Product",
        unitId: uomId,
      })
      .toPromise();
    const productId = productResult.data!.createProduct.id;

    const fakeCategoryId = "00000000-0000-0000-0000-000000000000";

    await expect(
      gql
        .mutation(updateProductCategories, { productId, categoryIds: [fakeCategoryId] })
        .toPromise(),
    ).rejects.toThrow();
  });

  it("Given a product ID that does not exist, the assignment fails with a product not found error", async () => {
    const ts = Date.now();

    const catResult = await gql
      .mutation(createCategoryIdOnly, { code: `ASSIGN-NOPROD-${ts}`, name: "No Product Category" })
      .toPromise();
    const categoryId = catResult.data!.createCategory.id;

    const fakeProductId = "00000000-0000-0000-0000-000000000000";

    await expect(
      gql
        .mutation(updateProductCategories, { productId: fakeProductId, categoryIds: [categoryId] })
        .toPromise(),
    ).rejects.toThrow();
  });
});
