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

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

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

const listProducts = graphql(`
  query ListProducts {
    products {
      edges {
        node {
          id
          name
          code
          status
        }
      }
    }
  }
`);

const listDraftProducts = graphql(`
  query ListDraftProducts {
    products(query: { status: { eq: DRAFT } }) {
      edges {
        node {
          id
          name
          code
          status
        }
      }
    }
  }
`);

const listProductsPaginated = graphql(`
  query ListProductsPaginated($first: Int, $after: String) {
    products(first: $first, after: $after) {
      edges {
        node {
          id
          name
          code
          status
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
`);

describe("viewer--browse-product-list", () => {
  const gql = createGraphQLClient(inject("url"), inject("token"));

  // Create our own products so this file is self-contained.
  // With parallel workers it may run before any product is created.
  // Two products cover the multi-page case.
  beforeAll(async () => {
    const unitsResult = await gql.query(listUnits, {}).toPromise();
    const unitId = unitsResult.data!.units.edges[0].node.id;
    const suffix = Date.now();
    await Promise.all(
      [0, 1].map((i) =>
        gql
          .mutation(createProduct, {
            code: `BROWSE-VIEWER-${suffix}-${i}`,
            name: `Browse Viewer Product ${i}`,
            unitId,
          })
          .toPromise(),
      ),
    );
  });

  it("Given products exist, when the list is opened by a viewer, then products are displayed without edit or management actions.", async () => {
    const result = await gql.query(listProducts, {}).toPromise();

    expect(result.data?.products.edges.length).toBeGreaterThan(0);
    const product = result.data?.products.edges[0].node;
    expect(product?.id).toBeDefined();
    expect(product?.name).toBeDefined();
    expect(product?.code).toBeDefined();
    expect(product?.status).toBeDefined();
  });

  it("Given a status filter is applied, when the list is rendered, then only products with that status are shown.", async () => {
    const result = await gql.query(listDraftProducts, {}).toPromise();

    for (const edge of result.data?.products.edges ?? []) {
      expect(edge.node.status).toBe("DRAFT");
    }
  });

  it("Given multiple pages, when pagination is used, then the correct page of products is displayed.", async () => {
    const firstPage = await gql.query(listProductsPaginated, { first: 1 }).toPromise();

    expect(firstPage.data?.products.edges.length).toBeGreaterThan(0);

    const endCursor = firstPage.data?.products.pageInfo.endCursor;
    if (firstPage.data?.products.pageInfo.hasNextPage && endCursor) {
      const secondPage = await gql
        .query(listProductsPaginated, { first: 1, after: endCursor })
        .toPromise();

      expect(secondPage.data?.products.edges.length).toBeGreaterThan(0);
      expect(secondPage.data?.products.edges[0].node.id).not.toBe(
        firstPage.data?.products.edges[0].node.id,
      );
    }
  });
});
