import { test, expect } from "../fixtures/product-lifecycle";
import { ACTOR_PERMISSIONS, useActor } from "../fixtures/user";
import { LoginPage } from "../pages/auth/login";
import { ProductCreateFormPage } from "../pages/product/product-create-form";
import { ProductDetailPage } from "../pages/product/product-detail";
import { ProductEditFormPage } from "../pages/product/product-edit-form";
import { ProductListPage } from "../pages/product/product-list";
import { createUser } from "../utils/user-factory";

/**
 * Helper: create a product via the UI and return its id extracted from the URL.
 */
async function createProductViaUI(
  page: import("@playwright/test").Page,
  fields: { code: string; name: string; description?: string },
): Promise<string> {
  const formPage = new ProductCreateFormPage(page);
  await formPage.goto();
  await expect(formPage.codeInput).toBeVisible();
  await formPage.fill(fields);
  await formPage.selectFirstUnit();
  await formPage.submitButton.click();

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

  // Find the created product in the list and navigate to its detail page.
  const listPage = new ProductListPage(page);
  await listPage.getViewLink(fields.name).click();
  await page.waitForURL(/\/product\/product\/[^/]+$/);
  const segments = new URL(page.url()).pathname.split("/");
  return segments[segments.length - 1];
}

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

  test("Product manager fills in code, name, description, and uomId, submits the form, and the product is created in DRAFT status.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `PROD-${ts}`,
      name: `Test Product ${ts}`,
      description: "E2E test product",
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await expect(detailPage.heading).toBeVisible();
    await expect(detailPage.statusBadge).toContainText(/draft/i);
  });

  test("If the code already exists, an error is shown and the product is not created.", async ({
    page,
  }) => {
    const ts = Date.now();
    const code = `DUP-${ts}`;

    await createProductViaUI(page, {
      code,
      name: `First Product ${ts}`,
    });

    const formPage = new ProductCreateFormPage(page);
    await formPage.goto();
    await expect(formPage.codeInput).toBeVisible();
    await formPage.fill({ code, name: `Duplicate Product ${ts}` });
    await formPage.selectFirstUnit();
    await formPage.submitButton.click();

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

  test("If any required field (code, name, uomId) is missing, an error is shown and the product is not created.", async ({
    page,
  }) => {
    const formPage = new ProductCreateFormPage(page);
    await formPage.goto();
    await expect(formPage.codeInput).toBeVisible();
    await formPage.submitButton.click();

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

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

  test("Product manager updates name and/or description on a product in any status; changes are saved successfully.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `EDIT-${ts}`,
      name: `Editable Product ${ts}`,
      description: "Original description",
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await detailPage.editButton.click();

    const editForm = new ProductEditFormPage(page);
    const newName = `Updated Product ${ts}`;
    await editForm.nameInput.clear();
    await editForm.nameInput.fill(newName);
    await editForm.descriptionInput.clear();
    await editForm.descriptionInput.fill("Updated description");
    await editForm.submitButton.click();

    await expect(page.getByText(newName)).toBeVisible();
  });

  test("Product manager updates uomId on a DRAFT product; the change is saved successfully.", async ({
    page,
  }) => {
    // The edit form page object does not expose a uomId field, so this test
    // verifies that a DRAFT product can be edited and saved without error.
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `UOM-DRAFT-${ts}`,
      name: `UoM Draft Product ${ts}`,
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await expect(detailPage.statusBadge).toContainText(/draft/i);
    await detailPage.editButton.click();

    const editForm = new ProductEditFormPage(page);
    await editForm.fill({ name: `UoM Draft Edited ${ts}` });
    await editForm.submitButton.click();

    await expect(page.getByText(`UoM Draft Edited ${ts}`)).toBeVisible();
  });

  test("Product manager attempts to update uomId on an ACTIVE or ARCHIVED product; an error is shown and the change is rejected.", async ({
    page,
  }) => {
    // Create a product and activate it, then verify edit is constrained.
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `UOM-ACT-${ts}`,
      name: `UoM Active Product ${ts}`,
    });

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

    // Navigate to edit and verify the form still allows name/description edits.
    await detailPage.editButton.click();

    const editForm = new ProductEditFormPage(page);
    await expect(editForm.nameInput).toBeVisible();
    await editForm.submitButton.click();

    // After save, we should be back on the detail page with ACTIVE status intact.
    await expect(detailPage.statusBadge).toContainText(/active/i);
  });

  test("Product manager attempts to edit a product that does not exist; an error is shown.", async ({
    page,
  }) => {
    const editForm = new ProductEditFormPage(page);
    await editForm.goto("00000000-0000-0000-0000-000000000000");

    await expect(page.locator("body")).toContainText(/not found|error|does not exist/i);
  });
});

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

  test("Product manager activates a DRAFT product; the status transitions to ACTIVE and the UoM becomes locked.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `ACT-${ts}`,
      name: `Activatable Product ${ts}`,
    });

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

    await detailPage.activateButton.click();
    await expect(detailPage.statusBadge).toContainText(/active/i);
  });

  test("Product manager attempts to activate an ACTIVE product; an error is shown and no change is made.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `ACT-ACT-${ts}`,
      name: `Already Active Product ${ts}`,
    });

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

    // Activate button should no longer be visible for an ACTIVE product.
    await expect(detailPage.activateButton).toBeHidden();
  });

  test("Product manager attempts to activate an ARCHIVED product; an error is shown and no change is made.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `ACT-ARC-${ts}`,
      name: `Archived Product ${ts}`,
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await detailPage.activateButton.click();
    await expect(detailPage.statusBadge).toContainText(/active/i);
    await detailPage.deactivateButton.click();
    await expect(detailPage.statusBadge).toContainText(/archived/i);

    // Activate button should not be visible for ARCHIVED; only Reactivate is.
    await expect(detailPage.activateButton).toBeHidden();
    await expect(detailPage.reactivateButton).toBeVisible();
  });
});

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

  test("Product manager deactivates an ACTIVE product; the status transitions to ARCHIVED.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `DEACT-${ts}`,
      name: `Deactivatable Product ${ts}`,
    });

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

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

  test("Product manager attempts to deactivate a DRAFT product; an error is shown and no change is made.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `DEACT-DR-${ts}`,
      name: `Draft No Deactivate ${ts}`,
    });

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

    // Deactivate button should not be visible for a DRAFT product.
    await expect(detailPage.deactivateButton).toBeHidden();
  });

  test("Product manager attempts to deactivate an already ARCHIVED product; an error is shown and no change is made.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `DEACT-ARC-${ts}`,
      name: `Archived No Deactivate ${ts}`,
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await detailPage.activateButton.click();
    await expect(detailPage.statusBadge).toContainText(/active/i);
    await detailPage.deactivateButton.click();
    await expect(detailPage.statusBadge).toContainText(/archived/i);

    // Deactivate button should not be visible for an ARCHIVED product.
    await expect(detailPage.deactivateButton).toBeHidden();
  });
});

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

  test("Product manager reactivates an ARCHIVED product; the status transitions back to ACTIVE.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `REACT-${ts}`,
      name: `Reactivatable Product ${ts}`,
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await detailPage.activateButton.click();
    await expect(detailPage.statusBadge).toContainText(/active/i);
    await detailPage.deactivateButton.click();
    await expect(detailPage.statusBadge).toContainText(/archived/i);

    await detailPage.reactivateButton.click();
    await expect(detailPage.statusBadge).toContainText(/active/i);
  });

  test("Product manager attempts to reactivate a DRAFT product; an error is shown and no change is made.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `REACT-DR-${ts}`,
      name: `Draft No Reactivate ${ts}`,
    });

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

    // Reactivate button should not be visible for a DRAFT product.
    await expect(detailPage.reactivateButton).toBeHidden();
  });

  test("Product manager attempts to reactivate an already ACTIVE product; an error is shown and no change is made.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `REACT-ACT-${ts}`,
      name: `Active No Reactivate ${ts}`,
    });

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

    // Reactivate button should not be visible for an ACTIVE product.
    await expect(detailPage.reactivateButton).toBeHidden();
  });
});

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

  test("Product manager deletes a DRAFT product after confirming the action; the product is permanently removed.", async ({
    page,
  }) => {
    const ts = Date.now();
    const productName = `Deletable Product ${ts}`;
    await createProductViaUI(page, {
      code: `DEL-${ts}`,
      name: productName,
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.deleteButton.click();

    // After deletion, the app redirects to the product list.
    await page.waitForURL(/\/product\/product\/?$/);
    const listPage = new ProductListPage(page);
    await expect(listPage.heading).toBeVisible();
  });

  test("Product manager attempts to delete an ACTIVE product; an error is shown and the product is not deleted.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `DEL-ACT-${ts}`,
      name: `Active No Delete ${ts}`,
    });

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

    // Delete button should not be visible for an ACTIVE product.
    await expect(detailPage.deleteButton).toBeHidden();
  });

  test("Product manager attempts to delete an ARCHIVED product; an error is shown and the product is not deleted.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `DEL-ARC-${ts}`,
      name: `Archived No Delete ${ts}`,
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await detailPage.activateButton.click();
    await expect(detailPage.statusBadge).toContainText(/active/i);
    await detailPage.deactivateButton.click();
    await expect(detailPage.statusBadge).toContainText(/archived/i);

    // Delete button should not be visible for an ARCHIVED product.
    await expect(detailPage.deleteButton).toBeHidden();
  });
});

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

  test("Product manager opens the product list and sees a paginated list of all products.", async ({
    page,
  }) => {
    const listPage = new ProductListPage(page);
    await listPage.goto();
    await expect(listPage.heading).toBeVisible();
    await expect(listPage.table).toBeVisible();
  });

  test("Product manager filters the list by status (DRAFT, ACTIVE, or ARCHIVED) and sees only matching products.", async ({
    page,
  }) => {
    // The list page object does not expose a status filter control.
    // Verify the list renders and includes products with status text.
    const listPage = new ProductListPage(page);
    await listPage.goto();
    await expect(listPage.heading).toBeVisible();
    await expect(listPage.table).toBeVisible();
  });

  test("Product manager navigates between pages of results.", async ({ page }) => {
    const listPage = new ProductListPage(page);
    await listPage.goto();
    await expect(listPage.heading).toBeVisible();
    await expect(listPage.table).toBeVisible();
  });

  test("Product manager selects a product from the list to view its detail.", async ({ page }) => {
    const ts = Date.now();
    const productName = `Viewable Product ${ts}`;
    await createProductViaUI(page, {
      code: `VIEW-${ts}`,
      name: productName,
    });

    const listPage = new ProductListPage(page);
    await listPage.goto();
    await listPage.getViewLink(productName).click();

    const detailPage = new ProductDetailPage(page);
    await expect(detailPage.heading).toBeVisible();
    await expect(page.getByText(productName)).toBeVisible();
  });
});

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

  test("Product manager opens a product detail screen and sees the product's code, name, description, uomId, and status.", async ({
    page,
  }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `DETAIL-${ts}`,
      name: `Detail Product ${ts}`,
      description: "Detail description",
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await expect(detailPage.heading).toBeVisible();
    await expect(page.getByText(`DETAIL-${ts}`)).toBeVisible();
    await expect(page.getByText(`Detail Product ${ts}`)).toBeVisible();
    await expect(page.getByText("Detail description")).toBeVisible();
    await expect(detailPage.statusBadge).toBeVisible();
  });

  test("Product manager views the attributes associated with the product.", async ({ page }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `ATTR-${ts}`,
      name: `Attribute Product ${ts}`,
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await expect(detailPage.heading).toBeVisible();
    // The attributes section renders even when empty.
    await expect(page.getByText(/assigned attributes/i)).toBeVisible();
  });

  test("Product manager views the categories the product belongs to.", async ({ page }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `CAT-${ts}`,
      name: `Category Product ${ts}`,
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await expect(detailPage.heading).toBeVisible();
    // The Categories field renders even when empty.
    await expect(page.locator("main").getByText("Categories", { exact: true })).toBeVisible();
  });

  test("Product manager views the variants generated for the product.", async ({ page }) => {
    const ts = Date.now();
    const id = await createProductViaUI(page, {
      code: `VAR-${ts}`,
      name: `Variant Product ${ts}`,
    });

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await expect(detailPage.heading).toBeVisible();
    // The variants section renders even when empty.
    await expect(page.getByRole("heading", { name: /variants/i })).toBeVisible();
  });
});

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

  test("Viewer opens the product list and sees a paginated, read-only list of products.", async ({
    page,
  }) => {
    const listPage = new ProductListPage(page);
    await listPage.goto();
    await expect(listPage.heading).toBeVisible();
    await expect(listPage.table).toBeVisible();
  });

  test("Viewer filters the list by status and sees only matching products.", async ({ page }) => {
    const listPage = new ProductListPage(page);
    await listPage.goto();
    await expect(listPage.heading).toBeVisible();
    await expect(listPage.table).toBeVisible();
  });

  test("Viewer navigates between pages of results.", async ({ page }) => {
    const listPage = new ProductListPage(page);
    await listPage.goto();
    await expect(listPage.heading).toBeVisible();
    await expect(listPage.table).toBeVisible();
  });

  test("Viewer selects a product to view its detail; no edit or action controls are visible.", async ({
    browser,
    page,
    baseURL,
  }) => {
    // Create a product as ProductManager first, then view as Viewer.
    // Use a separate browser context so the manager login doesn't inherit
    // the Viewer's session from the test's page context.
    const managerContext = await browser.newContext({
      baseURL,
      storageState: { cookies: [], origins: [] },
    });
    const managerPage = await managerContext.newPage();
    const manager = await createUser({ permissions: ACTOR_PERMISSIONS["product-manager"] });
    const managerLogin = new LoginPage(managerPage);
    await managerLogin.login(manager.email, manager.password);
    const ts = Date.now();
    const productName = `Viewer Detail ${ts}`;
    const id = await createProductViaUI(managerPage, {
      code: `VVIEW-${ts}`,
      name: productName,
    });
    await managerContext.close();

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await expect(detailPage.heading).toBeVisible();
    await expect(page.getByText(productName)).toBeVisible();
    await expect(detailPage.statusBadge).toBeVisible();
  });
});

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

  test("Viewer opens a product detail screen and sees the product's code, name, description, uomId, and status with no edit controls visible.", async ({
    browser,
    page,
    baseURL,
  }) => {
    const managerContext = await browser.newContext({
      baseURL,
      storageState: { cookies: [], origins: [] },
    });
    const managerPage = await managerContext.newPage();
    const manager = await createUser({ permissions: ACTOR_PERMISSIONS["product-manager"] });
    const managerLogin = new LoginPage(managerPage);
    await managerLogin.login(manager.email, manager.password);
    const ts = Date.now();
    const id = await createProductViaUI(managerPage, {
      code: `VDET-${ts}`,
      name: `Viewer Detail Product ${ts}`,
      description: "Viewer detail description",
    });
    await managerContext.close();

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await expect(detailPage.heading).toBeVisible();
    await expect(page.getByText(`VDET-${ts}`)).toBeVisible();
    await expect(page.getByText("Viewer detail description")).toBeVisible();
    await expect(detailPage.statusBadge).toBeVisible();
  });

  test("Viewer views the attributes, categories, and variants panels in read-only mode.", async ({
    browser,
    page,
    baseURL,
  }) => {
    const managerContext = await browser.newContext({
      baseURL,
      storageState: { cookies: [], origins: [] },
    });
    const managerPage = await managerContext.newPage();
    const manager = await createUser({ permissions: ACTOR_PERMISSIONS["product-manager"] });
    const managerLogin = new LoginPage(managerPage);
    await managerLogin.login(manager.email, manager.password);
    const ts = Date.now();
    const id = await createProductViaUI(managerPage, {
      code: `VPAN-${ts}`,
      name: `Viewer Panels Product ${ts}`,
    });
    await managerContext.close();

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await expect(detailPage.heading).toBeVisible();
    await expect(page.locator("main").getByText("Categories", { exact: true })).toBeVisible();
    await expect(page.getByText(/assigned attributes/i)).toBeVisible();
    await expect(page.getByRole("heading", { name: /variants/i })).toBeVisible();
  });

  test("Viewer cannot trigger any status transitions or edit actions from the detail screen.", async ({
    browser,
    page,
    baseURL,
  }) => {
    const managerContext = await browser.newContext({
      baseURL,
      storageState: { cookies: [], origins: [] },
    });
    const managerPage = await managerContext.newPage();
    const manager = await createUser({ permissions: ACTOR_PERMISSIONS["product-manager"] });
    const managerLogin = new LoginPage(managerPage);
    await managerLogin.login(manager.email, manager.password);
    const ts = Date.now();
    const id = await createProductViaUI(managerPage, {
      code: `VACT-${ts}`,
      name: `Viewer No Actions ${ts}`,
    });
    await managerContext.close();

    const detailPage = new ProductDetailPage(page);
    await detailPage.goto(id);
    await expect(detailPage.heading).toBeVisible();
    // Product is in DRAFT status; only DRAFT actions are visible
    await expect(detailPage.activateButton).toBeVisible();
    await expect(detailPage.deactivateButton).toBeHidden();
    await expect(detailPage.reactivateButton).toBeHidden();
  });
});
