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

// w044-enter-pos-full reproduced it: press "Enter in POS" on Canary Mosaic and the button
// does not move, no in-POS badge appears, and a reload brings back the same "Enter in POS".
// But "nothing changed" has TWO explanations and only one of them is the product's fault:
//   (a) the press reached the server and the screen simply never says so  -> a real defect
//   (b) my click never landed at all                                      -> my harness
// So this walk is built to tell them apart. It watches the wire while it presses, and it
// presses on BOTH surfaces that carry the control - the Orders-tab order card and the
// Tables-tab table sheet - because on Anant the relabel to "Re-enter in POS" was seen on
// the card, and it is entirely possible one surface reports state and the other does not.
const RID  = "28ae37ab-706c-4e20-83d7-52f5d2dff747";   // Canary Mosaic - dev
const T    = table(10097);
const SLUG = "w044b-enter-pos-discriminator";

const clean = (s) => (s || "").replace(/\s+/g, " ").trim();
const posBtn = (p) => p.locator('button, [role="button"]')
  .filter({ hasText: /(re-?)?enter(ing)?\s+(in\s+)?pos/i });
const labels = async (loc) => {
  const out = []; const n = await loc.count();
  for (let i = 0; i < Math.min(n, 8); i++) out.push(clean(await loc.nth(i).innerText()));
  return out;
};

test(SLUG, async ({ browser }) => {
  const jr = journal(SLUG);
  jr.target({ app: "patron mosaic_v2 + waiter", baseUrl: WAITER, restaurantId: RID, table: T });
  jr.note("**The question:** when the server presses *Enter in POS* and the screen does not change, did the press fail — or did it succeed silently? I watch the network while I press, so the answer is not a guess. I press on both places the control appears.");

  const cD = await roleContext(browser, jr, "diner");
  const d = await cD.newPage();
  const cW = await roleContext(browser, jr, "waiter", "Desktop Chrome");
  const w = await cW.newPage();

  // Everything the waiter's browser sends while it is on this board.
  const wire = [];
  w.on("request", r => { if (r.method() !== "GET") wire.push({ phase: "req", m: r.method(), u: r.url().slice(0, 200) }); });
  w.on("response", async r => {
    if (r.request().method() !== "GET" || /pos|order/i.test(r.url()))
      wire.push({ phase: "res", status: r.status(), m: r.request().method(), u: r.url().slice(0, 200) });
  });
  const since = () => wire.length;
  const sliceFrom = (i) => wire.slice(i).filter(x => x.m !== "GET" || /pos/i.test(x.u));

  let verdict = "INCOMPLETE", why = "", leftBehind = "nothing";
  const seen = {};
  try {
    await jr.soft("DINER", "I order one drink at my table", async id => {
      await arriveAsDiner(d, RID, T);
      const adds = d.locator('[data-testid^="m2-add-"]');
      if (!(await adds.count())) throw new Error("nothing on the menu can be added");
      seen.dish = await adds.first().getAttribute("data-testid");
      await adds.first().evaluate(el => el.click());
      await d.waitForTimeout(2500);
      const open = d.getByTestId("m2-view-order");
      if (await open.count()) await open.first().evaluate(el => el.click());
      else await d.getByTestId("m2-dock-cart").first().evaluate(el => el.click());
      await d.waitForTimeout(2500);
      const btn = d.getByTestId("order-now-btn");
      if (!(await btn.count())) throw new Error("no order-now-btn in the basket");
      seen.orderCta = clean(await btn.first().innerText());
      await btn.first().evaluate(el => el.click());
      await d.waitForTimeout(7000);
      const t = await look(d, jr, "diner", id, "my order is placed");
      return `ordered via "${seen.orderCta}" | ${clean(t).slice(0,200)}`;
    });

    const a = account("canaryMosaicWaiter");
    await jr.soft("WAITER", "the server signs in and my order is on the Orders tab", async id => {
      await signInWaiter(w, a.email, a.password);
      await w.waitForTimeout(9000);
      await w.getByTestId("tab-orders").click();
      await w.waitForTimeout(6000);
      const t = await look(w, jr, "waiter", id, "the Orders tab");
      seen.ordersText = clean(t).slice(0, 500);
      seen.cardBtns = await labels(posBtn(w));
      seen.cardActionIds = await w.evaluate(() => Array.from(
        document.querySelectorAll('[data-testid^="order-status-action-"],[data-testid^="pos-entered-"]'))
        .map(e => e.getAttribute("data-testid")));
      if (!seen.cardBtns.length) throw new Error(`no till control on the Orders tab. ids seen: ${JSON.stringify(seen.cardActionIds)} | ${seen.ordersText.slice(0,220)}`);
      return `Orders tab till control(s) ${JSON.stringify(seen.cardBtns)} | ids ${JSON.stringify(seen.cardActionIds)}`;
    });

    await jr.soft("WAITER", "I press it on the order card — and the network shows whether that reached the restaurant", async id => {
      const i = since();
      await posBtn(w).first().evaluate(el => el.click());
      await w.waitForTimeout(9000);
      seen.wireOnCardPress = sliceFrom(i).slice(0, 12);
      const t = await look(w, jr, "waiter", id, "Orders tab straight after pressing");
      seen.cardBtnsAfter = await labels(posBtn(w));
      seen.cardBadgeAfter = await w.locator('[data-testid^="pos-entered-"]').count();
      seen.cardTextAfter = clean(t).slice(0, 400);
      const wrote = seen.wireOnCardPress.some(x => x.phase === "res" && x.status < 400 && x.m !== "GET");
      if (!wrote) throw new Error(`the press sent NOTHING the restaurant could act on. Traffic in that window: ${JSON.stringify(seen.wireOnCardPress)}`);
      return `the press DID reach the restaurant: ${JSON.stringify(seen.wireOnCardPress)} | button now ${JSON.stringify(seen.cardBtnsAfter)} | in-POS badges ${seen.cardBadgeAfter}`;
    });

    await jr.soft("WAITER", "having pressed it, the card now tells me the order is in the till", async id => {
      const same = JSON.stringify(seen.cardBtnsAfter) === JSON.stringify(seen.cardBtns);
      if (same && !seen.cardBadgeAfter)
        throw new Error(`the card is unchanged: still ${JSON.stringify(seen.cardBtnsAfter)}, no in-POS badge. The order went to the till and the server has no way to know.`);
      return `card moved: ${JSON.stringify(seen.cardBtns)} -> ${JSON.stringify(seen.cardBtnsAfter)} | badges ${seen.cardBadgeAfter}`;
    });

    await jr.soft("WAITER", "the same is true in the table's own sheet", async id => {
      await w.getByTestId("tab-tables").click();
      await w.waitForTimeout(4000);
      const card = w.getByTestId(`table-card-${T}`);
      if (!(await card.count())) throw new Error(`table ${T} is not on the Tables tab`);
      await card.first().click();
      await w.waitForTimeout(6000);
      const t = await look(w, jr, "waiter", id, `table ${T} sheet after the press`);
      seen.sheetBtns = await labels(posBtn(w));
      seen.sheetBadge = await w.locator('[data-testid^="pos-entered-"]').count();
      seen.sheetText = clean(t).slice(0, 400);
      if (/enter in pos/i.test(seen.sheetBtns.join("|")) && !/re-?enter/i.test(seen.sheetBtns.join("|")) && !seen.sheetBadge)
        throw new Error(`the sheet also still offers "Enter in POS" with no in-POS mark: ${JSON.stringify(seen.sheetBtns)}`);
      return `sheet control(s) ${JSON.stringify(seen.sheetBtns)} | badges ${seen.sheetBadge}`;
    });

    await jr.soft("WAITER", "and it is still true after a reload, so it was written down somewhere", async id => {
      await w.reload({ waitUntil: "domcontentloaded" });
      await w.waitForTimeout(9000);
      await w.getByTestId("tab-orders").click();
      await w.waitForTimeout(6000);
      const t = await look(w, jr, "waiter", id, "Orders tab after a reload");
      seen.reloadBtns = await labels(posBtn(w));
      seen.reloadBadge = await w.locator('[data-testid^="pos-entered-"]').count();
      seen.reloadText = clean(t).slice(0, 400);
      if (JSON.stringify(seen.reloadBtns) === JSON.stringify(seen.cardBtns) && !seen.reloadBadge)
        throw new Error(`after a reload the board is exactly as it was before I pressed: ${JSON.stringify(seen.reloadBtns)}, no in-POS badge`);
      return `after reload ${JSON.stringify(seen.reloadBtns)} | badges ${seen.reloadBadge}`;
    });

    verdict = "WALKED";
    why = "Told apart 'the press failed' from 'the press succeeded silently'.";
  } catch (e) {
    verdict = "INCOMPLETE"; why = `the walk stopped: ${e.message}`;
  } finally {
    leftBehind = `Table ${T} on Canary Mosaic: one guest, one placed order, one press of Enter in POS. Table left open.`;
    jr.note("### The waiter's network traffic while pressing\n\n```json\n" + JSON.stringify(wire.slice(0, 80), null, 1).slice(0, 5000) + "\n```");
    jr.note("### Everything I read off the screens\n\n```json\n" + JSON.stringify(seen, null, 1).slice(0, 5000) + "\n```");
    jr.note(`> **What I left behind:** ${leftBehind}`);
    await closeRole(cD, jr, "diner"); await closeRole(cW, jr, "waiter");
    jr.finish(verdict, why);
  }
});
