/* WITNESS · mosaic_v2-multi
   The promise: several people at one table share a cart without the app double-counting
   anyone, the waiter sees them as distinct diners rather than one blob, a diner can cancel
   or adopt an item from the shared cart, and someone who sits down late still sees what was
   already ordered.

   WHERE: Anant, table 10 — the one table where I have a real waiter to look at.           */
import { test, expect } from "@playwright/test";
import { journal, look, arriveAsDiner, signInWaiter, DWELL, roleContext, closeRole } from "../lib-witness.mjs";

const RID = "097c229c-a1cb-4ba7-b603-800e46936133";
const TABLE = 10;
const phone = dir => ({ viewport:{width:390,height:844}, hasTouch:true, isMobile:true,
                        recordVideo:{ dir, size:{width:390,height:844} } });
const count = t => { const m = t.match(/(\d+)\s+ITEMS?/i); return m ? Number(m[1]) : null; };

test("mosaic_v2-multi", async ({ browser }) => {
  const jr = journal("mosaic_v2-multi");
  jr.note("**Where:** Anant, table 10.");
  jr.note("**Who:** three of us at one table — me (P1), my friend (P2), and someone who "
        + "joins late (P3) — plus Daniel the waiter.");
  jr.note("_The canary's last step needs an owner to disable a dish mid-meal. I will not "
        + "change a live restaurant's menu to make a test pass, so I do not walk that one._");

  const c1 = await roleContext(browser, jr, "patron-1");
  const c2 = await roleContext(browser, jr, "patron-2");
  const c3 = await roleContext(browser, jr, "patron-3");
  const cW = await roleContext(browser, jr, "waiter");
  const p1 = await c1.newPage(), p2 = await c2.newPage(), p3 = await c3.newPage(), w = await cW.newPage();
  let verdict = "INCOMPLETE", why = "";

  try {
    await jr.step("WAITER", "Daniel signs in and I note his board before we arrive", async id => {
      await signInWaiter(w, process.env.W_EMAIL, process.env.W_PW);
      const t = await look(w, jr, "waiter", id, "board before");
      expect(t).toContain("Anant");
      return t.slice(0, 160);
    });

    await jr.step("PATRON", "I sit down at table 10 and the menu opens", async id => {
      const g = await arriveAsDiner(p1, RID, TABLE, "mosaic_v2");
      const t = await look(p1, jr, "patron-1", id, "p1 menu");
      return `passed through: ${g.join(", ") || "no gates"}. ${t.slice(0, 110)}`;
    });

    await jr.step("PATRON", "My friend sits down at the same table and their menu opens too", async id => {
      const g = await arriveAsDiner(p2, RID, TABLE, "mosaic_v2");
      const t = await look(p2, jr, "patron-2", id, "p2 menu");
      return `passed through: ${g.join(", ") || "no gates"}. ${t.slice(0, 110)}`;
    });

    await jr.step("PATRON", "I add a dish, and my own cart says one item", async id => {
      await p1.locator('[data-testid^="m2-add-"]').first().click({ timeout: 25000 });
      const t = await look(p1, jr, "patron-1", id, "p1 added");
      const n = count(t);
      expect(n, "my cart should say exactly one item after one tap").toBe(1);
      return `my bar reads: ${(t.match(/\d+ ITEMS?[^.]*/i) || ["(no count)"])[0]}`;
    });

    await jr.soft("PATRON", "My friend's screen shows a badge telling them I'm at the table", async id => {
      await p2.reload({ waitUntil: "domcontentloaded" });
      const t = await look(p2, jr, "patron-2", id, "companion badge");
      const badge = await p2.locator('[data-testid*="companion"], [data-testid*="peer"], [data-testid*="glyph"]').count();
      if (!badge) throw new Error("I could not find anything on my friend's screen telling them "
                               + "someone else is at this table");
      return `${badge} companion marker(s) on screen; screen reads: ${t.slice(0, 150)}`;
    });

    await jr.step("PATRON", "My friend adds a dish too", async id => {
      await p2.locator('[data-testid^="m2-add-"]').first().click({ timeout: 25000 });
      const t = await look(p2, jr, "patron-2", id, "p2 added");
      return `their bar reads: ${(t.match(/\d+ ITEMS?[^.]*/i) || ["(no count)"])[0]}`;
    });

    await jr.step("PATRON", "My screen now shows the table's shared cart — and does NOT double-count me", async id => {
      await p1.locator('[data-testid="m2-view-order"], [data-testid="m2-dock-cart"]').first().click({ timeout: 25000 });
      const t = await look(p1, jr, "patron-1", id, "shared cart");
      const n = count(t);
      jr.note(`> **Cart inflation check:** two people each added one dish. My cart reads **${n ?? "?"}**. `
            + "Two is right; four would mean the app is counting each of us twice.");
      return t.slice(0, 240);
    });

    await jr.step("PATRON", "We send the order together", async id => {
      await p1.locator('[data-testid="order-now-btn"]').first().click({ timeout: 25000 });
      await p1.waitForTimeout(DWELL);
      const t = await look(p1, jr, "patron-1", id, "co-ordered");
      return t.slice(0, 200);
    });

    await jr.step("WAITER", "Daniel sees TWO diners at table 10, not one merged blob", async id => {
      for (let i = 0; i < 12; i++) {
        await w.reload({ waitUntil: "domcontentloaded" });
        await w.waitForTimeout(2500);
        if (/T10|table 10/i.test(await w.locator("body").innerText())) break;
      }
      const t = await look(w, jr, "waiter", id, "two diners");
      const guests = (t.match(/Guest/gi) || []).length;
      jr.note(`> **No-collapse check:** his board names a diner ${guests} time(s) for table 10.`);
      if (/all caught up|no active orders/i.test(t))
        throw new Error("Daniel's board still says nothing is waiting — our order never reached him");
      return t.slice(0, 260);
    });

    await jr.soft("PATRON", "My friend cancels one of the shared items", async id => {
      await p2.locator('[data-testid="m2-view-order"], [data-testid="m2-dock-cart"]').first().click({ timeout: 20000 });
      await p2.waitForTimeout(DWELL);
      const cancel = p2.locator('[data-testid*="cancel"]').first();
      if (!(await cancel.count()))
        throw new Error("I opened my friend's order and found no way to cancel an item — "
                      + `what I can see is: ${(await p2.locator("body").innerText()).replace(/\s+/g," ").slice(0,160)}`);
      await cancel.click({ timeout: 20000 });
      const t = await look(p2, jr, "patron-2", id, "cancelled item");
      return t.slice(0, 200);
    });

    await jr.step("PATRON", "Someone joins us late — and can see what we already ordered", async id => {
      const g = await arriveAsDiner(p3, RID, TABLE, "mosaic_v2");
      await p3.locator('[data-testid="m2-view-order"], [data-testid="m2-dock-cart"]').first()
              .click({ timeout: 25000 }).catch(() => {});
      const t = await look(p3, jr, "patron-3", id, "late joiner");
      jr.note("> **Late-joiner check:** they arrived after the order went in. If their screen is "
            + "empty, the table's history did not reach them.");
      return `passed through: ${g.join(", ") || "no gates"}. Their order screen shows: ${t.slice(0, 220)}`;
    });

    verdict = "WALKED"; why = "reached the end of everything a person can see without changing the menu";
  } catch (e) {
    verdict = "STOPPED"; why = String(e.message || e).split("\n")[0].slice(0, 300);
  } finally {
    jr.finish(verdict, why);
    await closeRole(c1, jr, "patron-1"); await closeRole(c2, jr, "patron-2"); await closeRole(c3, jr, "patron-3"); await closeRole(cW, jr, "waiter");
  }
});
