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

interface AttributeValue {
  id: string;
  label: string;
}

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

const createAttribute = graphql(`
  mutation CreateAttributeForVariants($code: String!, $name: String!, $valueLabels: [String!]) {
    createAttribute(code: $code, name: $name, valueLabels: $valueLabels) {
      id
      code
      name
    }
  }
`);

const getAttributeValues = graphql(`
  query GetAttributeValues($id: ID!) {
    productAttribute(id: $id) {
      id
      productAttributeValues {
        edges {
          node {
            id
            label
          }
        }
      }
    }
  }
`);

const createProduct = graphql(`
  mutation CreateProductForVariants(
    $code: String!
    $name: String!
    $unitId: ID!
    $attributes: [CreateProductInputAttributes!]
  ) {
    createProduct(code: $code, name: $name, unitId: $unitId, attributes: $attributes) {
      id
      code
      status
    }
  }
`);

const updateProductAttributes = graphql(`
  mutation UpdateProductAttributesForVariants(
    $productId: ID!
    $attributes: [UpdateProductAttributesInputAttributes!]!
  ) {
    updateProductAttributes(productId: $productId, attributes: $attributes) {
      productId
    }
  }
`);

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

const generateVariants = graphql(`
  mutation GenerateVariants($productId: ID!) {
    generateVariants(productId: $productId) {
      generatedCount
    }
  }
`);

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

  // Helper to set up an active product with two attributes, each having two values.
  async function setupActiveProductWithAttributes() {
    const ts = Date.now();

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

    const attr1Res = await gql
      .mutation(createAttribute, {
        code: `COLOR-${ts}`,
        name: "Color",
        valueLabels: ["Red", "Blue"],
      })
      .toPromise();
    const attr1Id = attr1Res.data!.createAttribute.id;

    const attr2Res = await gql
      .mutation(createAttribute, {
        code: `SIZE-${ts}`,
        name: "Size",
        valueLabels: ["S", "M"],
      })
      .toPromise();
    const attr2Id = attr2Res.data!.createAttribute.id;

    const attr1ValuesRes = await gql.query(getAttributeValues, { id: attr1Id }).toPromise();
    const attr2ValuesRes = await gql.query(getAttributeValues, { id: attr2Id }).toPromise();

    const attr1Values = attr1ValuesRes.data!.productAttribute!.productAttributeValues.edges.map(
      (e) => e.node,
    ) as AttributeValue[];
    const attr2Values = attr2ValuesRes.data!.productAttribute!.productAttributeValues.edges.map(
      (e) => e.node,
    ) as AttributeValue[];

    const attr1ValueId = attr1Values[0].id;
    const attr2ValueId = attr2Values[0].id;

    const productRes = await gql
      .mutation(createProduct, {
        code: `VPROD-${ts}`,
        name: "Variant Product",
        unitId,
        attributes: [
          { attributeId: attr1Id, valueId: attr1ValueId },
          { attributeId: attr2Id, valueId: attr2ValueId },
        ] as never,
      })
      .toPromise();
    const productId = productRes.data!.createProduct.id;

    // Assign all attribute values to the product.
    const allAssignments = [
      ...attr1Values.map((v: AttributeValue) => ({
        attributeId: attr1Id,
        valueId: v.id,
      })),
      ...attr2Values.map((v: AttributeValue) => ({
        attributeId: attr2Id,
        valueId: v.id,
      })),
    ];
    await gql
      .mutation(updateProductAttributes, {
        productId,
        attributes: allAssignments as never,
      })
      .toPromise();

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

    return { productId, unitId };
  }

  it("Given an ACTIVE product with two attributes each having two values, when variants are generated, then four Items are created with distinct auto-generated SKUs.", async () => {
    const { productId } = await setupActiveProductWithAttributes();

    const res = await gql.mutation(generateVariants, { productId }).toPromise();

    expect(res.data?.generateVariants.generatedCount).toBeDefined();
    expect(Number(res.data?.generateVariants.generatedCount)).toBe(4);
  });

  it("Given variants already generated, when generation is triggered again, then no duplicate Items are created.", async () => {
    const { productId } = await setupActiveProductWithAttributes();

    await gql.mutation(generateVariants, { productId }).toPromise();
    const res = await gql.mutation(generateVariants, { productId }).toPromise();

    // Second generation should not create additional variants.
    expect(Number(res.data?.generateVariants.generatedCount)).toBe(0);
  });

  it("Given a DRAFT or ARCHIVED product, when variant generation is triggered, then an error is shown.", async () => {
    const ts = Date.now();
    const unitsResult = await gql.query(listUnits, {}).toPromise();
    const unitId = unitsResult.data!.units.edges[0].node.id;

    const productRes = await gql
      .mutation(createProduct, {
        code: `DRAFT-${ts}`,
        name: "Draft Product",
        unitId,
      })
      .toPromise();
    const productId = productRes.data!.createProduct.id;

    await expect(gql.mutation(generateVariants, { productId }).toPromise()).rejects.toThrow();
  });

  it("Given an ACTIVE product with no attributes assigned, when variant generation is triggered, then an error is shown.", async () => {
    const ts = Date.now();
    const unitsResult = await gql.query(listUnits, {}).toPromise();
    const unitId = unitsResult.data!.units.edges[0].node.id;

    const productRes = await gql
      .mutation(createProduct, {
        code: `NOATTR-${ts}`,
        name: "No Attr Product",
        unitId,
      })
      .toPromise();
    const productId = productRes.data!.createProduct.id;

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

    await expect(gql.mutation(generateVariants, { productId }).toPromise()).rejects.toThrow();
  });
});
