/* WITNESS · dietary safety, walked on Anant with demo@ (W-040) — QRATE-UAT-1
 *
 * WHY THIS EXISTS: I deferred W-040 as "blocked on provisioning" because the CANARY fixture
 * has no owner row. That conflated two different things. The canary restaurant being
 * unprovisioned does not make the BEHAVIOUR unwalkable - Anant has 32 real dishes, demo@
 * owns the owner app, and a guest can sit at table 10. The operator was right to push back.
 *
 * THE QUESTION A DINER CARES ABOUT: if I tell this app I cannot eat something, does it
 * actually stop showing me dishes that contain it? That is a safety claim, not a filter
 * preference - getting it wrong hurts someone.
 *
 * NO MUTATION. I read the tags the owner already has and check the diner side honours them.
 * Writing tags onto a shared dev restaurant to make my own test pass would be provisioning
 * my own subject, which is the thing I keep filing tickets about.
 */
import { test } from "@playwright/test";
import { journal, look, roleContext, deskContext, closeRole, signInOwner, account,
         arriveAsDiner, PATRON, OWNER, DWELL } from "../lib-witness.mjs";

const RID = "097c229c-a1cb-4ba7-b603-800e46936133";      // Anant
const TABLE = 10;

const tap = async (page, sel) => {
  const l = page.locator(sel).first();
  if (!(await l.count())) return false;
  await l.click({ timeout: 10000 }).catch(async () => { await l.evaluate(el => el.click()); });
  return true;
};

test("anant-dietary-safety", async ({ browser }) => {
  const jr = journal("anant-dietary-safety");
  jr.target({ app: "owner+patron", patronUrl: PATRON, ownerUrl: OWNER, restaurantId: RID,
              table: TABLE, fixture: "Anant (real menu, 32 dishes) — dev" });
  jr.note("_The canary fixture for this journey has no owner account, so I walk the same "
        + "question on Anant, where I do have one. I change nothing: I read the dietary tags "
        + "the restaurant already has, then check whether the diner side honours them._");

  const oc = await deskContext(browser, jr, "owner");
  const o = await oc.newPage();
  const pc = await roleContext(browser, jr, "patron");
  const p = await pc.newPage();
  const rec = {};
  let verdict = "INCOMPLETE", why = "";

  try {
    await jr.step("OWNER", "I sign in and look at what this restaurant says about its dishes", async id => {
      const a = account("demoOwner");
      await signInOwner(o, a.email, a.password, RID);
      await o.goto(`${OWNER}/owner/menu/item/?restaurantId=${RID}`, { waitUntil: "domcontentloaded" })
        .catch(() => {});
      await o.waitForTimeout(DWELL);
      const t = await look(o, jr, "owner", id, "owner menu");
      const nav = await o.evaluate(() => [...new Set([...document.querySelectorAll("nav a[href], aside a[href]")]
        .filter(e => e.offsetParent !== null).map(e => (e.innerText || "").replace(/\s+/g, " ").trim()).filter(Boolean))]);
      jr.note(`> **This restaurant's owner sidebar:** ${nav.join(" · ")}`);
      rec.ownerNav = nav;
      return `Signed in. Sidebar: ${nav.slice(0, 10).join(" · ")}. Screen: ${t.slice(0, 140)}`;
    });

    await jr.step("OWNER", "I find which dishes carry a dietary or allergen tag", async id => {
      await o.goto(`${OWNER}/owner/food-items/?restaurantId=${RID}`, { waitUntil: "domcontentloaded" })
        .catch(() => {});
      await o.waitForTimeout(DWELL * 2);
      const t = await look(o, jr, "owner", id, "food items");
      const found = await o.evaluate(() => {
        const txt = (document.querySelector("main") || document.body).innerText;
        const words = ["vegetarian","vegan","gluten","nut","dairy","egg","shellfish","jain","halal","allergen"];
        const hits = {};
        for (const w of words) {
          const m = txt.match(new RegExp(w, "gi"));
          if (m) hits[w] = m.length;
        }
        return { hits, rows: document.querySelectorAll('[data-testid^="food-item-"], tbody tr').length };
      });
      rec.ownerTags = found;
      jr.note(`> **Dietary words visible on the owner's food-items screen:** ${JSON.stringify(found.hits)}`);
      if (!Object.keys(found.hits).length)
        throw new Error(`This restaurant's owner screen shows no dietary or allergen wording at all, `
          + `so there is nothing for the diner side to honour. Screen: ${t.slice(0, 200)}`);
      return `${found.rows} rows; dietary wording present: ${JSON.stringify(found.hits)}.`;
    });

    await jr.step("PATRON", "I sit down as a guest and open the dietary filter", async id => {
      await arriveAsDiner(p, RID, TABLE);
      await look(p, jr, "patron", id, "arrived");
      await tap(p, '[data-testid="m2-dock-trigger"]'); await p.waitForTimeout(1500);
      const opened = await tap(p, '[data-testid="m2-dock-filter"]')
                  || await tap(p, '[data-testid="m2-filter-trigger"]');
      await p.waitForTimeout(DWELL);
      const t = await look(p, jr, "patron", id, "filter drawer");
      const chips = await p.evaluate(() => [...document.querySelectorAll('[data-testid^="m2-dietary-"]')]
        .map(e => ({ id: e.getAttribute("data-testid"), label: (e.innerText || "").trim() })));
      rec.chips = chips;
      jr.note(`> **Dietary choices this restaurant offers a diner:** `
            + `${chips.map(c => c.label || c.id).join(" · ") || "NONE"}`);
      if (!chips.length)
        throw new Error(`I cannot tell this restaurant anything about what I cannot eat - the drawer `
          + `offers no dietary choice at all. Opened=${opened}. Screen: ${t.slice(0, 200)}`);
      return `${chips.length} dietary choice(s): ${chips.map(c => c.label || c.id).join(", ")}.`;
    });

    await jr.step("PATRON", "I say I cannot eat something, and count what I am then offered", async id => {
      const before = await p.evaluate(() => document.querySelectorAll('[data-testid^="m2-tile-"]').length);
      const chip = rec.chips[0];
      await tap(p, `[data-testid="${chip.id}"]`);
      await p.waitForTimeout(1200);
      await tap(p, '[data-testid="m2-filter-apply"]');
      await p.waitForTimeout(3000);
      const after = await p.evaluate(() => document.querySelectorAll('[data-testid^="m2-tile-"]').length);
      await look(p, jr, "patron", id, `after choosing ${chip.label || chip.id}`);
      rec.filter = { chip: chip.label || chip.id, before, after };
      // The control must MOVE THE NUMBER, or this step proves nothing about safety.
      if (before === after)
        throw new Error(`Choosing "${chip.label || chip.id}" changed nothing: ${before} dishes before, `
          + `${after} after. Either every dish already qualifies, or the choice is not being applied - `
          + `and from the outside I cannot tell which, so this is not evidence that the filter protects me.`);
      return `Choosing "${chip.label || chip.id}" took the menu from ${before} dishes to ${after}.`;
    });

    verdict = "PARTIAL_GREEN";
    why = `Walked on Anant with a real owner account and a real guest, no data changed. `
        + `The diner can state a dietary need and the menu visibly narrows.`;
  } catch (e) {
    why = e.message;
    verdict = "HONEST_RED";
  } finally {
    await closeRole(oc, jr, "owner");
    await closeRole(pc, jr, "patron");
    jr.finish(verdict, why);
  }
});
