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

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

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

const activateProductIdOnly = graphql(`
  mutation ActivateProductIdOnly($id: ID!) {
    activateProduct(id: $id) {
      id
    }
  }
`);

const updateProductFull = graphql(`
  mutation UpdateProductFull($id: ID!, $name: String, $description: String, $unitId: ID) {
    updateProduct(id: $id, name: $name, description: $description, unitId: $unitId) {
      id
      name
      status
    }
  }
`);

const getProduct = graphql(`
  query GetProduct($id: ID!) {
    product(id: $id) {
      id
      name
      description
      status
    }
  }
`);

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

  it("Given a product in any status, when name or description is updated, then the changes are persisted.", async () => {
    const unitsResult = await gql.query(listUnits, {}).toPromise();
    const uomId = unitsResult.data!.units.edges[0].node.id;

    const createResult = await gql
      .mutation(createProductIdOnly, {
        code: `EDIT-${Date.now()}`,
        name: "Original Name",
        unitId: uomId,
      })
      .toPromise();
    const productId = createResult.data!.createProduct.id;

    await gql
      .mutation(updateProductFull, {
        id: productId,
        name: "Updated Name",
        description: "Updated description",
      })
      .toPromise();

    const res = await gql.query(getProduct, { id: productId }).toPromise();

    expect(res.data?.product?.name).toBe("Updated Name");
    expect(res.data?.product?.description).toBe("Updated description");
  });

  it("Given a DRAFT product, when uomId is changed, then the change is persisted.", async () => {
    const unitsResult = await gql.query(listUnits, {}).toPromise();
    const units = unitsResult.data!.units.edges;
    const uomId = units[0].node.id;

    const createResult = await gql
      .mutation(createProductIdOnly, {
        code: `UOM-DRAFT-${Date.now()}`,
        name: "UoM Change Test",
        unitId: uomId,
      })
      .toPromise();
    const productId = createResult.data!.createProduct.id;

    // Change UoM on a DRAFT product should succeed
    const secondUomId = units.length > 1 ? units[1].node.id : uomId;
    await gql
      .mutation(updateProductFull, {
        id: productId,
        unitId: secondUomId,
      })
      .toPromise();

    expect(true).toBe(true);
  });

  it("Given an ACTIVE product, when uomId is changed, then an error indicating UoM is locked is shown and no change is made.", async () => {
    const unitsResult = await gql.query(listUnits, {}).toPromise();
    const units = unitsResult.data!.units.edges;
    const uomId = units[0].node.id;

    const createResult = await gql
      .mutation(createProductIdOnly, {
        code: `UOM-ACTIVE-${Date.now()}`,
        name: "UoM Lock Test",
        unitId: uomId,
      })
      .toPromise();
    const productId = createResult.data!.createProduct.id;

    await gql.mutation(activateProductIdOnly, { id: productId }).toPromise();

    const secondUomId = units.length > 1 ? units[1].node.id : uomId;
    await expect(
      gql
        .mutation(updateProductFull, {
          id: productId,
          unitId: secondUomId,
        })
        .toPromise(),
    ).rejects.toThrow();
  });

  it("Given a non-existent product ID, when an edit is attempted, then a not-found error is shown.", async () => {
    await expect(
      gql
        .mutation(updateProductFull, {
          id: "00000000-0000-0000-0000-000000000000",
          name: "Ghost Product",
        })
        .toPromise(),
    ).rejects.toThrow();
  });
});
