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

const RID  = "7766d206-244c-576d-be4b-f5e746bf9832";
const T    = table(10096);
const SLUG = "w043-card-tap";

// The card screen renders a collapsed Stripe "Card" row and a greyed-out Pay button.
// A real diner would TAP that row. This walk does exactly that, and only that.
async function frameDump(p) {
  const out = [];
  for (const f of p.frames()) {
    if (!/js\.stripe\.com/i.test(f.url())) continue;
    const rec = { name: f.name(), kind: (f.url().match(/v3\/([a-z-]+)-[0-9a-f]{8,}/) || [])[1] || "?" };
    try {
      rec.text = (await f.locator("body").innerText().catch(() => "")).replace(/\s+/g, " ").slice(0, 160);
      rec.inputs = await f.locator("input").evaluateAll(els => els.map(e => ({
        name: e.getAttribute("name"), ph: e.getAttribute("placeholder"), type: e.type,
        vis: !!(e.offsetWidth || e.offsetHeight) })));
    } catch (e) { rec.err = String(e.message).split("\n")[0].slice(0, 100); }
    out.push(rec);
  }
  return out;
}

test(SLUG, async ({ browser }) => {
  const jr = journal(SLUG);
  jr.target({ app: "patron mosaic_v2", baseUrl: PATRON, restaurantId: RID, table: T });
  jr.note("**The question:** the card screen shows a white row that just says **Card**, and a greyed-out **Pay $4.57**. There is nothing to type into. A diner would tap the Card row. Does that open the card fields?");

  const c = await roleContext(browser, jr, "diner");
  const p = await c.newPage();
  let verdict = "INCOMPLETE", why = "";
  const seen = {};
  try {
    await jr.soft("DINER", "I order, ask for the check and take the card route", async id => {
      await arriveAsDiner(p, RID, T, "mosaic_v2");
      await p.locator('[data-testid^="m2-add-"]').first().evaluate(el => el.click());
      await p.waitForTimeout(2500);
      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);
      await p.getByTestId("layout-service-button").first().evaluate(el => el.click());
      await p.waitForTimeout(3000);
      await p.getByTestId("service-bubble-ready_for_bill").first().evaluate(el => el.click());
      await p.waitForTimeout(4000);
      const cover = p.getByTestId("settlement-cover");
      if (await cover.count()) { await cover.first().evaluate(el => el.click()); await p.waitForTimeout(3000); }
      const me = p.getByTestId("cover-scope-just-me");
      if (await me.count()) { await me.first().evaluate(el => el.click()); await p.waitForTimeout(3500); }
      await p.getByTestId("payhow-pay-now").first().evaluate(el => el.click());
      await p.waitForTimeout(12000);
      await p.getByTestId("pyc-continue").first().evaluate(el => el.click());
      await p.waitForTimeout(15000);
      const t = await look(p, jr, "diner", id, "the card screen as it arrives");
      seen.before = await frameDump(p);
      const btn = p.getByTestId("pyc-pay-btn");
      seen.payBefore = (await btn.count()) ? { text: (await btn.first().innerText()).trim(), disabled: await btn.first().isDisabled() } : null;
      if (!seen.before.length) throw new Error("no Stripe frame at all");
      return `frames ${JSON.stringify(seen.before)} | pay button ${JSON.stringify(seen.payBefore)}`;
    });

    await jr.soft("DINER", "I tap the row that says Card, the way anyone would", async id => {
      let tapped = null;
      for (const f of p.frames()) {
        if (!/js\.stripe\.com/i.test(f.url())) continue;
        const cand = f.getByText(/^\s*Card\s*$/i).first();
        if (await cand.count().catch(() => 0)) {
          await cand.click({ timeout: 10000 }).catch(async () => { await cand.evaluate(el => el.click()); });
          tapped = `text "Card" inside ${f.name()}`;
          break;
        }
      }
      if (!tapped) {
        const ifr = p.locator('iframe[name^="__privateStripeFrame"]');
        for (let i = 0; i < await ifr.count(); i++) {
          const b = await ifr.nth(i).boundingBox();
          if (b && b.height > 20) { await p.mouse.click(b.x + b.width / 2, b.y + b.height / 2); tapped = `centre of iframe #${i} (${Math.round(b.width)}x${Math.round(b.height)})`; break; }
        }
      }
      seen.tapped = tapped;
      await p.waitForTimeout(9000);
      await look(p, jr, "diner", id, "after tapping Card");
      seen.after = await frameDump(p);
      const btn = p.getByTestId("pyc-pay-btn");
      seen.payAfter = (await btn.count()) ? { text: (await btn.first().innerText()).trim(), disabled: await btn.first().isDisabled() } : null;
      const typeable = seen.after.flatMap(f => (f.inputs || []).filter(i => i.vis));
      if (!tapped) throw new Error("there was nothing on the card screen I could tap");
      if (!typeable.length) throw new Error(`tapped ${tapped} and still no card field appeared. Frames now: ${JSON.stringify(seen.after)}`);
      return `tapped ${tapped}; fields now ${JSON.stringify(typeable)}`;
    });

    await jr.soft("DINER", "I type a test card and the Pay button comes alive", async id => {
      let filled = [];
      for (const f of p.frames()) {
        if (!/js\.stripe\.com/i.test(f.url())) continue;
        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().catch(() => 0)) { await l.fill(val).catch(() => {}); filled.push(sel); }
        }
      }
      seen.filled = filled;
      await p.waitForTimeout(3000);
      await look(p, jr, "diner", id, "card typed in");
      const btn = p.getByTestId("pyc-pay-btn").first();
      seen.payFilled = { text: (await btn.innerText()).trim(), disabled: await btn.isDisabled() };
      if (!filled.length) throw new Error("card fields visible but none accepted a value");
      if (seen.payFilled.disabled) throw new Error(`card entered and the Pay button is STILL greyed out: ${JSON.stringify(seen.payFilled)}`);
      return `filled ${JSON.stringify(filled)}; pay button now ${JSON.stringify(seen.payFilled)} - I am stopping here rather than charging`;
    });

    verdict = "WALKED"; why = "Tapped the Card row on the payment screen and recorded what happened.";
  } catch (e) { why = String(e.message || e).split("\n")[0]; }
  finally {
    jr.note("### What the Stripe frames held, before and after the tap\n\n```json\n" + JSON.stringify(seen, null, 1).slice(0, 6000) + "\n```");
    jr.note(`> **What I left behind:** table ${T} on ${RID} — one guest, one placed order, one open check request. Nothing was charged.`);
    jr.finish(verdict, why);
    await closeRole(c, jr, "diner");
  }
  expect(verdict, why).not.toBe("INCOMPLETE");
});
