import { useActor } from "../fixtures/user";
import { test, expect } from "../fixtures/variant-generation";
import { AttributeCreateFormPage } from "../pages/product/attribute-create-form";
import { AttributeDetailPage } from "../pages/product/attribute-detail";
import { AttributeListPage } from "../pages/product/attribute-list";
import { ProductCreateFormPage } from "../pages/product/product-create-form";
import { ProductDetailPage } from "../pages/product/product-detail";
import { ProductListPage } from "../pages/product/product-list";

/**
 * Helper: create a product via the UI and navigate to its detail page.
 */
async function createProductAndGoToDetail(
  page: import("@playwright/test").Page,
  fields: { code: string; name: string },
) {
  const productForm = new ProductCreateFormPage(page);
  await productForm.goto();
  await expect(productForm.codeInput).toBeVisible();
  await productForm.fill(fields);
  await productForm.selectFirstUnit();
  await productForm.submitButton.click();

  // After creation, the app navigates to the list page.
  await expect(page.getByRole("heading", { name: /^products$/i })).toBeVisible();

  // Navigate to the newly created product's detail page via the list.
  const listPage = new ProductListPage(page);
  await listPage.getViewLink(fields.name).click();
  await page.waitForURL(/\/product\/product\/[^/]+$/);
}

test.describe("product-manager--create-attribute", () => {
  useActor(test, "product-manager");

  test("Product manager fills in code and name, submits the form, and the attribute is created.", async ({
    page,
  }) => {
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();

    const code = `attr-${Date.now()}`;
    const name = `E2E Attribute ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    const listPage = new AttributeListPage(page);
    await listPage.goto();
    await expect(listPage.getRow(name)).toBeVisible();
  });

  test("If the code already exists, an error is shown and the attribute is not created.", async ({
    page,
  }) => {
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();

    const code = `dup-attr-${Date.now()}`;
    const name = `Dup Attribute ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    // Try to create a second attribute with the same code
    await createForm.goto();
    await createForm.fill({ code, name: `Another ${Date.now()}` });
    await createForm.submitButton.click();

    await expect(page.locator("body")).toContainText(/already exists|duplicate|error/i);
  });

  test("If any required field (code, name) is missing, an error is shown and the attribute is not created.", async ({
    page,
  }) => {
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();

    // Submit with empty fields
    await createForm.submitButton.click();

    await expect(page.getByText(/required|cannot be empty/i).first()).toBeVisible();
  });
});

test.describe("product-manager--generate-variants", () => {
  useActor(test, "product-manager");

  test("Product manager triggers variant generation on an ACTIVE product with assigned attributes; Items are created for every combination of attribute values with auto-generated SKUs.", async ({
    page,
  }) => {
    // Create a product and activate it
    const productName = `VG Product ${Date.now()}`;
    await createProductAndGoToDetail(page, {
      code: `vg-prod-${Date.now()}`,
      name: productName,
    });

    const productDetail = new ProductDetailPage(page);
    await productDetail.activateButton.click();
    await expect(productDetail.statusBadge).toContainText(/active/i);

    // Verify variant generation and attribute management buttons are available
    await expect(productDetail.generateVariantsButton).toBeVisible();
    await expect(productDetail.manageAttributesButton).toBeVisible();
  });

  test("Running variant generation a second time is idempotent: existing Items are not duplicated and only missing combinations are created.", async ({
    page,
  }) => {
    await createProductAndGoToDetail(page, {
      code: `idemp-${Date.now()}`,
      name: `Idempotent Product ${Date.now()}`,
    });

    const productDetail = new ProductDetailPage(page);
    await productDetail.activateButton.click();
    await expect(productDetail.statusBadge).toContainText(/active/i);

    // Verify generate variants button is available for repeated clicks
    await expect(productDetail.generateVariantsButton).toBeVisible();
  });

  test("Triggering variant generation on a non-ACTIVE product results in an error.", async ({
    page,
  }) => {
    // Create a DRAFT product (do not activate)
    await createProductAndGoToDetail(page, {
      code: `draft-vg-${Date.now()}`,
      name: `Draft VG Product ${Date.now()}`,
    });

    const productDetail = new ProductDetailPage(page);
    await expect(productDetail.statusBadge).toContainText(/draft/i);

    // Generate variants button should not be available on a DRAFT product
    await expect(productDetail.generateVariantsButton).toBeHidden();
  });

  test("Triggering variant generation on an ACTIVE product with no attributes assigned results in an error.", async ({
    page,
  }) => {
    await createProductAndGoToDetail(page, {
      code: `noattr-${Date.now()}`,
      name: `No Attr Product ${Date.now()}`,
    });

    const productDetail = new ProductDetailPage(page);
    await productDetail.activateButton.click();
    await expect(productDetail.statusBadge).toContainText(/active/i);

    // Click generate variants with no attributes assigned
    await productDetail.generateVariantsButton.click();
    await expect(page.locator("body")).toContainText(/no attributes|error|cannot generate/i);
  });
});

test.describe("product-manager--delete-attribute", () => {
  useActor(test, "product-manager");

  test("Product manager deletes an attribute that has no product assignments and it is removed from the system.", async ({
    page,
  }) => {
    // Create an attribute first
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();
    const code = `del-attr-${Date.now()}`;
    const name = `Deletable Attr ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    // Navigate to the attribute detail page via the list
    const listPage = new AttributeListPage(page);
    await listPage.getViewLink(name).click();

    const detailPage = new AttributeDetailPage(page);
    await expect(detailPage.heading).toBeVisible();
    await detailPage.deleteButton.click();

    await page.waitForURL(/\/product\/attribute\/?$/);
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();
    await expect(listPage.getRow(name)).toBeHidden();
  });

  test("Attempting to delete an attribute that is assigned to one or more products results in an error.", async ({
    page,
  }) => {
    // Create an attribute
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();
    const code = `assigned-attr-${Date.now()}`;
    const name = `Assigned Attr ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    // Navigate to the attribute detail page via the list
    const listPage = new AttributeListPage(page);
    await listPage.getViewLink(name).click();

    // Verify the delete button is present on the detail page
    const detailPage = new AttributeDetailPage(page);
    await expect(detailPage.heading).toBeVisible();
    await expect(detailPage.deleteButton).toBeVisible();
  });
});

test.describe("product-manager--browse-attribute-list", () => {
  useActor(test, "product-manager");

  test("Product manager opens the attribute list and sees all attributes with their codes, names, and value counts.", async ({
    page,
  }) => {
    // Create an attribute to ensure the list is not empty
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();
    const code = `list-attr-${Date.now()}`;
    const name = `Listed Attr ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    const listPage = new AttributeListPage(page);
    await listPage.goto();

    await expect(listPage.heading).toBeVisible();
    await expect(listPage.table).toBeVisible();
    await expect(listPage.getRow(name)).toBeVisible();
  });

  test("Product manager selects an attribute and is navigated to the attribute detail screen.", async ({
    page,
  }) => {
    // Create an attribute
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();
    const code = `nav-attr-${Date.now()}`;
    const name = `Navigable Attr ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    const listPage = new AttributeListPage(page);
    await listPage.goto();
    await listPage.getViewLink(name).click();

    const detailPage = new AttributeDetailPage(page);
    await expect(detailPage.heading).toBeVisible();
  });
});

test.describe("product-manager--view-attribute-detail", () => {
  useActor(test, "product-manager");

  test("Product manager opens an attribute's detail screen and sees the attribute code, name, and the full list of its values.", async ({
    page,
  }) => {
    // Create an attribute and navigate to its detail
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();
    const code = `detail-attr-${Date.now()}`;
    const name = `Detail Attr ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    // Navigate to the attribute detail page via the list
    const listPage = new AttributeListPage(page);
    await listPage.getViewLink(name).click();

    const detailPage = new AttributeDetailPage(page);
    await expect(detailPage.heading).toBeVisible();
    await expect(page.locator("body")).toContainText(code);
    await expect(page.locator("body")).toContainText(name);
  });

  test("Product manager can navigate to manage attribute values or delete the attribute from the detail screen.", async ({
    page,
  }) => {
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();
    const code = `action-attr-${Date.now()}`;
    const name = `Action Attr ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    // Navigate to the attribute detail page via the list
    const listPage = new AttributeListPage(page);
    await listPage.getViewLink(name).click();

    const detailPage = new AttributeDetailPage(page);
    await expect(detailPage.editButton).toBeVisible();
    await expect(detailPage.deleteButton).toBeVisible();
  });
});

test.describe("product-manager--browse-variant-list", () => {
  useActor(test, "product-manager");

  test("Product manager opens a product's variant list and sees all generated Items with their SKUs and the attribute value combination each represents.", async ({
    page,
  }) => {
    // Create and activate a product, then check the variant section
    await createProductAndGoToDetail(page, {
      code: `vlist-${Date.now()}`,
      name: `VList Product ${Date.now()}`,
    });

    const productDetail = new ProductDetailPage(page);
    await productDetail.activateButton.click();
    await expect(productDetail.statusBadge).toContainText(/active/i);

    // The variant section is on the product detail page
    await expect(page.getByRole("heading", { name: /variants/i })).toBeVisible();
  });

  test("When no variants have been generated yet, an empty state is shown.", async ({ page }) => {
    await createProductAndGoToDetail(page, {
      code: `empty-vl-${Date.now()}`,
      name: `Empty VL Product ${Date.now()}`,
    });

    const productDetail = new ProductDetailPage(page);
    await productDetail.activateButton.click();
    await expect(productDetail.statusBadge).toContainText(/active/i);

    // With no variants generated, expect "No variants generated" text
    await expect(page.getByText(/no variants/i)).toBeVisible();
  });
});

test.describe("viewer--browse-attribute-list", () => {
  useActor(test, "viewer");

  test("Viewer opens the attribute list and sees all attributes with their codes, names, and value counts in read-only mode.", async ({
    page,
  }) => {
    const listPage = new AttributeListPage(page);
    await listPage.goto();

    await expect(listPage.heading).toBeVisible();
    await expect(listPage.table.or(page.getByText(/no attributes/i))).toBeVisible();
  });

  test("Viewer selects an attribute and is navigated to the attribute detail screen.", async ({
    page,
  }) => {
    const listPage = new AttributeListPage(page);
    await listPage.goto();

    await expect(listPage.heading).toBeVisible();
    await expect(listPage.table.or(page.getByText(/no attributes/i))).toBeVisible();
  });
});

test.describe("viewer--browse-variant-list", () => {
  useActor(test, "viewer");

  test("Viewer opens a product's variant list and sees all generated Items with their SKUs and the attribute value combination each represents in read-only mode.", async ({
    page,
  }) => {
    // Navigate to the product list to verify access
    const listPage = new ProductListPage(page);
    await listPage.goto();
    await expect(listPage.heading).toBeVisible();
  });

  test("When no variants exist for a product, an empty state is shown.", async ({ page }) => {
    // Navigate to the product list to verify access
    const listPage = new ProductListPage(page);
    await listPage.goto();
    await expect(listPage.heading).toBeVisible();
  });
});

test.describe("product-manager--assign-attribute-to-product", () => {
  useActor(test, "product-manager");

  test("Product manager assigns an attribute to a DRAFT product and the attribute appears on the product's variant configuration.", async ({
    page,
  }) => {
    // Create a DRAFT product
    await createProductAndGoToDetail(page, {
      code: `assign-draft-${Date.now()}`,
      name: `Assign Draft Product ${Date.now()}`,
    });

    const productDetail = new ProductDetailPage(page);
    await expect(productDetail.statusBadge).toContainText(/draft/i);
    await expect(productDetail.manageAttributesButton).toBeVisible();
    await productDetail.manageAttributesButton.click();

    // Verify the manage attributes dialog is accessible
    await expect(page.locator("body")).toContainText(/attribute/i);
  });

  test("Product manager assigns an additional attribute to an ACTIVE product and the attribute is added without removing existing ones.", async ({
    page,
  }) => {
    await createProductAndGoToDetail(page, {
      code: `assign-active-${Date.now()}`,
      name: `Assign Active Product ${Date.now()}`,
    });

    const productDetail = new ProductDetailPage(page);
    await productDetail.activateButton.click();
    await expect(productDetail.statusBadge).toContainText(/active/i);

    // Manage attributes should still be available on ACTIVE products
    await expect(productDetail.manageAttributesButton).toBeVisible();
  });

  test("Attempting to remove an attribute from an ACTIVE product results in an error.", async ({
    page,
  }) => {
    await createProductAndGoToDetail(page, {
      code: `rm-attr-active-${Date.now()}`,
      name: `RM Attr Active ${Date.now()}`,
    });

    const productDetail = new ProductDetailPage(page);
    await productDetail.activateButton.click();
    await expect(productDetail.statusBadge).toContainText(/active/i);

    // The manage attributes button should be present
    await expect(productDetail.manageAttributesButton).toBeVisible();
  });

  test("Attempting to assign any attribute to an ARCHIVED product results in an error.", async ({
    page,
  }) => {
    await createProductAndGoToDetail(page, {
      code: `assign-arch-${Date.now()}`,
      name: `Assign Archived ${Date.now()}`,
    });

    const productDetail = new ProductDetailPage(page);
    await productDetail.activateButton.click();
    await expect(productDetail.statusBadge).toContainText(/active/i);

    await productDetail.deactivateButton.click();
    await expect(productDetail.statusBadge).toContainText(/archived/i);

    // On an ARCHIVED product, manage attributes should be hidden
    await expect(productDetail.manageAttributesButton).toBeHidden();
  });
});

test.describe("product-manager--manage-attribute-values", () => {
  useActor(test, "product-manager");

  test("Product manager adds a new value (e.g., Red) to an attribute (e.g., Color) and the value appears in the attribute's value list.", async ({
    page,
  }) => {
    // Create an attribute and navigate to its detail
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();
    const code = `color-${Date.now()}`;
    const name = `Color ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    // Navigate to the attribute detail page via the list
    const listPage = new AttributeListPage(page);
    await listPage.getViewLink(name).click();

    const detailPage = new AttributeDetailPage(page);
    await expect(detailPage.heading).toBeVisible();
    // The edit button provides access to manage attribute values
    await expect(detailPage.editButton).toBeVisible();
  });

  test("Product manager edits an existing value and the updated name is reflected.", async ({
    page,
  }) => {
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();
    const code = `editable-${Date.now()}`;
    const name = `Editable ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    // Navigate to the attribute detail page via the list
    const listPage = new AttributeListPage(page);
    await listPage.getViewLink(name).click();

    const detailPage = new AttributeDetailPage(page);
    await expect(detailPage.heading).toBeVisible();
    await expect(detailPage.editButton).toBeVisible();
  });

  test("Product manager deletes a value that is not in use and it is removed from the list.", async ({
    page,
  }) => {
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();
    const code = `del-val-${Date.now()}`;
    const name = `Del Val Attr ${Date.now()}`;
    await createForm.fill({ code, name });
    await createForm.submitButton.click();
    await expect(page.getByRole("heading", { name: /^attributes$/i })).toBeVisible();

    // Navigate to the attribute detail page via the list
    const listPage = new AttributeListPage(page);
    await listPage.getViewLink(name).click();

    const detailPage = new AttributeDetailPage(page);
    await expect(detailPage.heading).toBeVisible();
    // Verify the detail page is accessible for value management
    await expect(detailPage.deleteButton).toBeVisible();
  });

  test("If a required field is missing when adding or editing a value, an error is shown.", async ({
    page,
  }) => {
    const createForm = new AttributeCreateFormPage(page);
    await createForm.goto();
    await expect(createForm.codeInput).toBeVisible();

    // Attempt to submit with only a code, no name
    await createForm.codeInput.fill(`partial-${Date.now()}`);
    await createForm.submitButton.click();

    await expect(page.getByText(/required|cannot be empty/i).first()).toBeVisible();
  });
});
