/* WITNESS · micro-kitchen-windowless-checkout (W-024) — QRATE-UAT-1
 *
 * What the board claims passed, in a person's words:
 *   1 the kitchen has something orderable · 2 I can reach the menu and cart it
 *   3 pressing Place order opens a CHECKOUT · 4 the checkout says we will arrange collection
 *   5 confirming actually places the order
 *
 * So the question is narrow and answerable: after I press Place order, am I looking at a
 * CHECKOUT, and can I finish? A previous walk of the sibling journey found the button opens
 * a who-are-you gate instead. This time I do not stop there — I try to get past it the way
 * a new customer would (make an account), and only if that dead-ends do I fall back to a
 * confirmed account.
 *
 * SAFETY: this is a micro kitchen, so there is no table to leave open. I press NOTHING that
 * pays. If the checkout wants a card, I stop and say so.
 */
import { test } from "@playwright/test";
import { journal, look, roleContext, closeRole, account, PATRON, DWELL } from "../lib-witness.mjs";
import fs from "node:fs";

const RID = JSON.parse(fs.readFileSync("/workspace/uat/runs/wave0/fixtures.json", "utf8"))["micro-kitchen-windowless"];
const TABLE = 10491;
const OUT = "/workspace/uat/wave1/session-recon";

async function answerDietary(page, ms = 15000) {
  const gate = page.locator('[data-testid="m2-dietary-gate"]');
  try { await gate.first().waitFor({ state: "visible", timeout: ms }); } catch { return false; }
  const no = page.locator('[data-testid="m2-dietary-gate-no"]').first();
  await no.click({ timeout: 10000 }).catch(async () => { await no.evaluate(el => el.click()).catch(() => {}); });
  await gate.first().waitFor({ state: "hidden", timeout: 10000 }).catch(() => {});
  await page.waitForTimeout(1500);
  return true;
}
const tap = async (page, sel) => {
  const l = page.locator(sel).first();
  if (!(await l.count())) return false;
  await l.click({ timeout: 8000 }).catch(async () => { await l.evaluate(el => el.click()); });
  return true;
};
const tids = page => page.evaluate(() =>
  [...new Set([...document.querySelectorAll("[data-testid]")].map(e => e.getAttribute("data-testid")))].sort());
/** Anything a person could read as "how and when do you want it". Named widely on purpose. */
const fulfil = page => page.evaluate(() =>
  [...document.querySelectorAll("[data-testid]")].map(e => e.getAttribute("data-testid"))
    .filter(t => /pickup|pick-up|delivery|deliver|window|slot|collect|fulfil|schedule|when|time|asap|address|date/i.test(t)).sort());
/** Anything that would take money. I press none of these. */
const payish = page => page.evaluate(() =>
  [...document.querySelectorAll("[data-testid]")].map(e => e.getAttribute("data-testid"))
    .filter(t => /pay|card|stripe|checkout-pay|settle/i.test(t)).sort());

test("micro-kitchen-windowless-checkout", async ({ browser }) => {
  const jr = journal("micro-kitchen-windowless-checkout");
  jr.target({ app: "patron", url: PATRON, restaurantId: RID, table: TABLE,
              fixture: "Canary Micro Kitchen Windowless — dev" });
  jr.note("_A kitchen with no collection windows. I want to cart something and check out. "
        + "The board says pressing Place order opens a checkout that tells me collection will "
        + "be arranged, and that confirming sends it._");
  jr.note("**I press nothing that pays.** If the checkout asks for a card, that is where I stop.");

  const ctx = await roleContext(browser, jr, "patron");
  const page = await ctx.newPage();
  const rec = {};
  let verdict = "INCOMPLETE", why = "";

  try {
    await jr.step("PATRON", "There is something here I could actually order", async id => {
      await page.goto(`${PATRON}/menu?id=${RID}&table=${TABLE}`, { waitUntil: "domcontentloaded" });
      await page.waitForTimeout(DWELL);
      const asked = await answerDietary(page);
      await look(page, jr, "patron", id, "arrival");
      const n = await page.locator('[data-testid^="m2-tile-"]').count();
      if (!n) throw new Error("Nothing on the menu.");
      rec.dishes = n;
      return `${n} dish(es) I could tap${asked ? "; it asked about allergies first" : ""}.`;
    });

    await jr.step("PATRON", "I put one in my basket", async id => {
      await page.locator('[data-testid^="m2-tile-"]').first().evaluate(el => el.click());
      await page.waitForTimeout(DWELL);
      await page.locator('[data-testid="composition-add"]').first().click({ timeout: 20000 });
      await page.waitForTimeout(DWELL);
      await look(page, jr, "patron", id, "one in the basket");
      const n = (await page.locator('[data-testid="m2-dock-cart-count"]').first().innerText().catch(() => "")).trim();
      if (!n || n === "0") throw new Error("Nothing landed in the basket.");
      return `Basket shows ${n}.`;
    });

    await jr.step("PATRON", "Pressing Place order opens a CHECKOUT", async id => {
      await tap(page, '[data-testid="m2-dock-trigger"]'); await page.waitForTimeout(2000);
      await tap(page, '[data-testid="m2-dock-cart"]');    await page.waitForTimeout(DWELL);
      await answerDietary(page, 3000);
      await look(page, jr, "patron", id, "my basket");
      const before = await tids(page);
      const btn = page.locator('[data-testid="order-now-btn"]').first();
      if (!(await btn.count())) throw new Error("There is no Place order button in my basket.");
      const label = (await btn.innerText()).replace(/\s+/g, " ").trim();
      await btn.click({ timeout: 20000 });
      await page.waitForTimeout(DWELL);
      const t = await look(page, jr, "patron", id, "what Place order opened");
      const after = await tids(page);
      rec.appeared = after.filter(x => !before.includes(x));
      rec.gate = await page.locator('[data-testid="signup-gate"]').count();
      rec.gateText = rec.gate ? (await page.locator('[data-testid="signup-gate"]').first().innerText()).replace(/\s+/g, " ").trim() : "";
      jr.note(`> **What pressing "${label}" put on screen:** ${rec.appeared.join(", ") || "nothing new"}`);
      if (rec.gate)
        throw new Error(`It did not open a checkout. It opened a who-are-you panel: "${rec.gateText.slice(0, 160)}"`);
      return `A checkout opened. New on screen: ${rec.appeared.join(", ")}. Page reads: ${t.slice(0, 200)}`;
    });

    jr.finish("WALKED", "reached the checkout");
    verdict = "WALKED";
  } catch (e) {
    why = String(e.message || e).split("\n")[0];

    /* Not the end of the walk. A real new customer would now make an account, so I do too. */
    await jr.soft("PATRON", "So I try to make an account, the way a new customer would", async id => {
      await tap(page, '[data-testid="signup-gate-use-email"]');
      await page.waitForTimeout(2500);
      await tap(page, '[data-testid="signup-gate-mode-signup"]');
      await page.waitForTimeout(1500);
      const email = `uat-w024-${Date.now()}@qrate.test`;
      await page.locator('[data-testid="signup-gate-email"]').first().fill(email);
      await page.locator('[data-testid="signup-gate-password"]').first().fill(`Uat!${Date.now().toString().slice(-8)}aA`);
      await look(page, jr, "patron", id, "new account filled in");
      const sub = page.locator('[data-testid="signup-gate-email-submit"]').first();
      if (await sub.isDisabled().catch(() => false))
        throw new Error("The button stays disabled with both boxes filled.");
      await sub.click({ timeout: 20000 });
      await page.waitForTimeout(DWELL * 2);
      const t = await look(page, jr, "patron", id, "after asking for an account");
      const err = (await page.locator('[data-testid="signup-gate-error"]').first().innerText().catch(() => "")).trim();
      const code = await page.locator('[data-testid="signup-gate-code"]').count();
      rec.signup = { err, code, screen: t.slice(0, 300) };
      if (err) throw new Error(`It refused to make me an account: "${err}"`);
      if (code) throw new Error("It now wants a code emailed to that address, which I cannot read. A real customer could; I cannot.");
      return `No error and no code box. Screen now: ${t.slice(0, 220)}`;
    });

    await jr.soft("PATRON", "Failing that, I sign in with an account that already exists", async id => {
      const a = account("diner");
      if (await page.locator('[data-testid="signup-gate-mode-signin"]').count()) {
        await tap(page, '[data-testid="signup-gate-mode-signin"]'); await page.waitForTimeout(1500);
      }
      await page.locator('[data-testid="signup-gate-email"]').first().fill(a.email);
      await page.locator('[data-testid="signup-gate-password"]').first().fill(a.password);
      await look(page, jr, "patron", id, "existing account filled in");
      await page.locator('[data-testid="signup-gate-email-submit"]').first().click({ timeout: 20000 });
      await page.waitForTimeout(DWELL * 2);
      const t = await look(page, jr, "patron", id, "after signing in");
      const err = (await page.locator('[data-testid="signup-gate-error"]').first().innerText().catch(() => "")).trim();
      rec.signin = { err, screen: t.slice(0, 300), stillGated: await page.locator('[data-testid="signup-gate"]').count() };
      if (err) throw new Error(`Refused: "${err}"`);
      return `Screen now: ${t.slice(0, 220)}`;
    });

    await jr.soft("PATRON", "If I am through, the checkout tells me collection will be arranged", async id => {
      const t = await look(page, jr, "patron", id, "the checkout");
      rec.checkout = { fulfil: await fulfil(page), pay: await payish(page), tids: await tids(page),
                       text: t.slice(0, 900) };
      jr.note(`> **On this screen:** fulfilment controls ${rec.checkout.fulfil.join(", ") || "none"}; `
            + `payment controls ${rec.checkout.pay.join(", ") || "none"}`);
      if (await page.locator('[data-testid="signup-gate"]').count())
        throw new Error("I am still at the who-are-you panel, so there is no checkout to read.");
      const says = /arrange|collect|pick ?up|ready|contact/i.test(t);
      if (!says) throw new Error(`Nothing on screen says anything about arranging collection. It reads: "${t.slice(0, 200)}"`);
      return `It reads: "${t.slice(0, 300)}"`;
    });

    jr.finish("STOPPED", why);
  } finally {
    fs.writeFileSync(`${OUT}/w024.json`, JSON.stringify(rec, null, 1));
    await closeRole(ctx, jr, "patron");
  }
});
