import { Page, expect } from "@playwright/test"
import { clickToReveal, selectOption, comboboxByLabel } from "../support/ui"

// Locators verified against Frontend/erp-system/app/dashboard/receiving/grn/new/page.tsx
// and .../grn/[id]/page.tsx. Things that DOM inspection caught and a placeholder-only guess
// would not have:
//   - The Qty/Unit cost/Disc%/VAT% <Input type="number"> cells carry no accessible name
//     (no htmlFor/aria-label) - located by column position within the row instead.
//   - On a PO-based line (line.poLineId set) Item/UOM render as plain text, not a Select -
//     fillFirstLine() only touches the item combobox when one is actually present (checked by
//     role alone, not name - see below).
//   - Every page here intermittently throws a real React hydration error (#418) that blanks a
//     random subset of Select triggers' placeholder text for that page's lifetime, WITHOUT
//     affecting their sibling <Label> or role="combobox" attribute (support/ui.ts has the full
//     writeup). So triggers are located via comboboxByLabel() (label + role, no name lookup)
//     instead of getByRole("combobox", { name }) - the row-scoped item/uom/bin combos have no
//     adjacent label and are instead found by position, which is equally immune to the bug.
export class GrnNewPage {
  constructor(private readonly page: Page) {}

  async goto() {
    await this.page.goto("/dashboard/receiving/grn/new")
  }

  async useDirectReceipt() {
    await clickToReveal(
      this.page.getByRole("button", { name: /direct receipt/i }),
      comboboxByLabel(this.page, "Vendor")
    )
  }

  async useAgainstPo() {
    await clickToReveal(
      this.page.getByRole("button", { name: /against po/i }),
      comboboxByLabel(this.page, "Purchase order")
    )
  }

  async selectVendor(name: string) {
    await selectOption(this.page, comboboxByLabel(this.page, "Vendor"), name)
  }

  async selectWarehouse(name: string) {
    await selectOption(this.page, comboboxByLabel(this.page, "Warehouse"), name)
  }

  /** `docNo` is what the PO option renders (`{docNo} — Vendor #{vendorId} ({status})`) - not the numeric id. */
  async selectPurchaseOrder(docNo: string) {
    await selectOption(this.page, comboboxByLabel(this.page, "Purchase order"), new RegExp(docNo))
  }

  private firstRow() {
    return this.page.locator("table tbody tr").first()
  }

  /**
   * Item is only a combobox when the row is NOT tied to a PO line (`line.poLineId` gates the
   * cell in the source - a PO line renders it as plain text instead). Checked per-cell
   * (td:nth(0)) rather than "row has any combobox", since the Bin/Hold-status cells always
   * have one regardless of PO mode - a row-wide check would false-positive on a PO line and
   * select the wrong control.
   *
   * td:nth(1) is still the UOM column, but it is now a read-only label showing the chosen
   * item's base UOM: lines carry no unit of their own, so there is nothing to pick. The
   * column was kept rather than removed, which is why every index below is unchanged.
   */
  async fillFirstLine(opts: { item?: string; qty: number; unitCost?: number }) {
    const row = this.firstRow()
    const cells = row.locator("td")
    if (opts.item) {
      const itemCombo = cells.nth(0).getByRole("combobox")
      if (await itemCombo.count()) {
        await selectOption(this.page, itemCombo, opts.item)
      }
    }
    const numberInputs = row.locator('input[type="number"]')
    await numberInputs.nth(0).fill(String(opts.qty)) // Qty
    if (opts.unitCost !== undefined) {
      await numberInputs.nth(1).fill(String(opts.unitCost)) // Unit cost
    }
  }

  async submit() {
    await this.page.getByRole("button", { name: /create grn/i }).click()
  }
}

export class GrnDetailPage {
  constructor(private readonly page: Page) {}

  async gotoById(grnId: number) {
    await this.page.goto(`/dashboard/receiving/grn/${grnId}`)
  }

  /** GrnStatusBadge/HoldStatusBadge render the raw status string verbatim - exact match avoids
   *  matching prose like "Confirmed — stock layers created" in the post-confirm success panel. */
  async expectStatus(status: "Draft" | "Confirmed") {
    await expect(this.page.getByText(status, { exact: true })).toBeVisible()
  }

  async confirm() {
    await this.page.getByRole("button", { name: /confirm grn/i }).click()
  }

  async releaseFirstOnHoldLine() {
    await this.page.getByRole("button", { name: /^release$/i }).first().click()
  }

  async rejectFirstOnHoldLine() {
    await this.page.getByRole("button", { name: /^reject$/i }).first().click()
  }

  async expectCreateReturnLink() {
    await expect(this.page.getByRole("link", { name: /create return/i })).toBeVisible()
  }
}
