import { test, expect } from "@playwright/test";
import { journal, look, arriveAsDiner, roleContext, closeRole, table, PATRON, DWELL } from "../lib-witness.mjs";

const RID  = "7766d206-244c-576d-be4b-f5e746bf9832";
const T    = table(10090);
const SLUG = "w043-unpaid-discriminator";
const sheet = async (p, tid) => {
  const l = p.getByTestId(tid);
  return (await l.count()) ? (await l.first().innerText()).replace(/\s+/g, " ").trim() : "";
};
async function openBubble(p) {
  const b = p.getByTestId("layout-service-button");
  if (await b.count()) await b.first().evaluate(el => el.click());
  else await p.evaluate(() => window.dispatchEvent(new CustomEvent("qrate:open-service-bubble")));
  await p.waitForTimeout(3000);
}

test(SLUG, async ({ browser }) => {
  const jr = journal(SLUG);
  jr.target({ app: "patron mosaic_v2", baseUrl: PATRON, restaurantId: RID, table: T });
  jr.note("**A discriminating re-run.** On the previous table both drinks cost within a penny of each other ($3.50 and $3.49), so when my friend's check asked for $3.81 - exactly what I had already paid - I could not tell whether she was being shown her own bill or mine. This time she orders a **$7.00** drink and I order a **$3.50** one. Her check must read about $7.62. If it reads $3.81 she is being asked to pay my bill.\n\n**The original claim.** The board's final step says that after one diner pays, only the unpaid part is left. On table 10097 I paid for my own share and then failed to read my friend's check — because I looked for the wrong control, not because the product withheld one. She IS offered *Pay for your order / Pay for the Table*. This walk redoes that tail properly and reads what her check actually says.");

  const c1 = await roleContext(browser, jr, "diner-1");
  const c2 = await roleContext(browser, jr, "diner-2");
  const p1 = await c1.newPage(), p2 = await c2.newPage();
  let verdict = "INCOMPLETE", why = "", paid = false;
  const seen = {};
  try {
    await jr.soft("BOTH", "two of us order a drink each", async id => {
      await arriveAsDiner(p1, RID, T, "mosaic_v2");
      await arriveAsDiner(p2, RID, T, "mosaic_v2");
      await p1.locator('[data-testid^="m2-add-"]').first().evaluate(el => el.click());
      await p2.locator('[data-testid^="m2-add-"]').nth(2).evaluate(el => el.click());  // a $7.00 IPA, not a $3.49 tea - the prices must differ enough to tell the two diners apart
      await p1.waitForTimeout(2500); await p2.waitForTimeout(2500);
      for (const p of [p1, p2]) {
        await p.getByTestId("m2-view-order").first().evaluate(el => el.click());
        await p.waitForTimeout(2500);
        await p.getByTestId("order-now-btn").first().evaluate(el => el.click());
        await p.waitForTimeout(7000);
      }
      return await look(p1, jr, "diner-1", id, "both ordered");
    });

    await jr.soft("DINER 1", "I pay for my own items with a card", async id => {
      await openBubble(p1);
      await p1.getByTestId("service-bubble-ready_for_bill").first().evaluate(el => el.click());
      await p1.waitForTimeout(4000);
      const cover = p1.getByTestId("settlement-cover");
      if (await cover.count()) { await cover.first().evaluate(el => el.click()); await p1.waitForTimeout(3000); }
      const me = p1.getByTestId("cover-scope-just-me");
      if (await me.count()) { await me.first().evaluate(el => el.click()); await p1.waitForTimeout(3500); }
      await p1.getByTestId("payhow-pay-now").first().evaluate(el => el.click());
      await p1.waitForTimeout(12000);
      seen.p1Check = await sheet(p1, "pay-your-check");
      await p1.getByTestId("pyc-continue").first().evaluate(el => el.click());
      await p1.waitForTimeout(14000);
      let f = null;
      for (const fr of p1.frames()) {
        if (!/js\.stripe\.com/i.test(fr.url())) continue;
        const row = fr.getByText(/^\s*Card\s*$/i).first();
        if (await row.count().catch(() => 0)) { await row.click({ timeout: 10000 }).catch(async () => row.evaluate(e => e.click())); f = fr; break; }
      }
      if (!f) throw new Error("no Card row to tap");
      await p1.waitForTimeout(6000);
      for (const [sel, val] of [['input[name="number"]', "4242424242424242"], ['input[name="expiry"]', "1234"],
                                ['input[name="cvc"]', "123"], ['input[name="postalCode"]', "42424"]]) {
        const l = f.locator(sel).first(); if (await l.count()) await l.fill(val);
      }
      await p1.waitForTimeout(2500);
      const btn = p1.getByTestId("pyc-pay-btn").first();
      if (await btn.isDisabled()) throw new Error("Pay still greyed out after entering the card");
      await btn.click(); paid = true;
      await p1.waitForTimeout(30000);
      await look(p1, jr, "diner-1", id, "paid");
      seen.receipt = await sheet(p1, "pay-your-check");
      if (!/paid/i.test(seen.receipt)) throw new Error(`no confirmation: ${seen.receipt}`);
      return seen.receipt;
    });

    await jr.soft("DINER 2", "my friend is told where the bill now stands", async id => {
      await p2.reload(); await p2.waitForTimeout(DWELL);
      await openBubble(p2);
      const cp = p2.getByTestId("service-bubble-ready_for_bill");
      if (!(await cp.count())) throw new Error(`no Check Please for her: ${await sheet(p2, "service-bubble")}`);
      await cp.first().evaluate(el => el.click());
      await p2.waitForTimeout(5000);
      seen.p2Offer = await sheet(p2, "service-bubble");
      seen.p2Ids = await p2.evaluate(() => Array.from(document.querySelectorAll("[data-testid]"))
        .map(e => e.getAttribute("data-testid")).filter(x => /settle|cover|payhow|split|pay/.test(x)).sort());
      await look(p2, jr, "diner-2", id, "what she is offered after I paid");
      return `HER SHEET: "${seen.p2Offer}" | controls ${JSON.stringify(seen.p2Ids)}`;
    });

    await jr.soft("DINER 2", "she chooses to pay her own order and sees only what is unpaid", async id => {
      const own = p2.locator('[data-testid="settlement-pay-own"], [data-testid="split-invitation-pay-own"]').first();
      let used = null;
      if (await own.count()) { used = "settlement-pay-own"; await own.evaluate(el => el.click()); }
      else {
        const byText = p2.getByText(/pay for (your|my) order/i).first();
        if (!(await byText.count())) throw new Error(`no "pay for your order" control: ${JSON.stringify(seen.p2Ids)}`);
        await byText.evaluate(el => el.click()); used = 'text "Pay for your order"';
      }
      await p2.waitForTimeout(5000);
      const pay = p2.getByTestId("payhow-pay-now");
      if (await pay.count()) { await pay.first().evaluate(el => el.click()); await p2.waitForTimeout(12000); }
      seen.p2Check = await sheet(p2, "pay-your-check");
      seen.p2Lines = await p2.getByTestId("pyc-line-item").allInnerTexts().catch(() => []);
      seen.p2AmountDue = await sheet(p2, "pyc-amount-due");
      seen.p2Caption = await sheet(p2, "pyc-line-items-caption");
      seen.p2Total = await sheet(p2, "pyc-total");
      await look(p2, jr, "diner-2", id, "her check after I paid mine");
      if (!seen.p2AmountDue) throw new Error(`her check shows no amount. Sheet: "${seen.p2Check}" (used ${used})`);
      return `used ${used} | caption "${seen.p2Caption}" | lines ${JSON.stringify(seen.p2Lines)} | amount due ${seen.p2AmountDue} | total ${seen.p2Total}`;
    });

    verdict = "WALKED"; why = "One diner paid by card; the other diner's check was then read.";
  } catch (e) { why = String(e.message || e).split("\n")[0]; }
  finally {
    jr.note("### Everything read off the screens\n\n```json\n" + JSON.stringify(seen, null, 1).slice(0, 6000) + "\n```");
    jr.note(`> **What I left behind:** table ${T} on ${RID} — two guests, two placed orders, ${paid ? "one completed test-mode card payment (pk_test_, 4242) for diner 1's share" : "no payment"}. Diner 2's drink is unpaid.`);
    jr.finish(verdict, why);
    await closeRole(c1, jr, "diner-1"); await closeRole(c2, jr, "diner-2");
  }
  expect(verdict, why).not.toBe("INCOMPLETE");
});
