/* WITNESS · mosaic_v2-payments-config (W-057) — QRATE-UAT-1
 *
 * The journey's five steps: an OWNER baseline for payments_enabled; an OWNER round-trip of the
 * accepted payment methods with a 409 guard; then three PATRON steps that are assertions about
 * a JSON summary flag, a Stripe intent 409, and a client_secret. Those last three have no
 * screen - they are API contract - so what a person can actually check here is the OWNER half:
 * can the owner of this restaurant see, and set, whether card payments are on and which methods
 * are accepted?
 *
 * SAFETY, absolute: the canary fixtures are live Stripe merchants on dev. I press NOTHING that
 * pays and I SAVE NOTHING. This walk only reads. Toggling a kill switch on a shared fixture
 * would also give the builder a false red on the next canary run, so I would not do it even if
 * it were safe.
 */
import { test } from "@playwright/test";
import { journal, look, deskContext, roleContext, closeRole, signInOwner, account,
         arriveAsDiner, OWNER, PATRON, DWELL } from "../lib-witness.mjs";
import fs from "node:fs";

const F = JSON.parse(fs.readFileSync("/workspace/uat/runs/wave0/fixtures.json", "utf8"));
const RID = F["mosaic_v2-electronic-pay"];
const TABLE = 9952;
const OUT = "/workspace/uat/wave1/session-recon";

const mainText = page => page.evaluate(() =>
  (document.querySelector("main") || document.body).innerText.replace(/\s+/g, " ").trim());
/** Every control on the page that is not the shell chrome. */
const realControls = page => page.evaluate(() =>
  [...document.querySelectorAll("button,[role=button],[role=switch],input,select,textarea,a[href]")]
    .filter(e => e.offsetParent !== null)
    .map(e => ({ t: (e.innerText || e.value || "").replace(/\s+/g, " ").trim().slice(0, 44),
                 tid: e.getAttribute("data-testid") || null, type: e.type || e.tagName.toLowerCase(),
                 checked: e.type === "checkbox" ? e.checked : null }))
    .filter(c => !/^nav-|restaurant-switcher|user-menu|mobile-nav/.test(c.tid || ""))
    .filter(c => !/^(Home|Food Items|Menu|Tables\/Staff|Personalize|Insights|Orders|Collection|Payments|Skip to main content|QRate)$/.test(c.t))
    .filter(c => !/^Canary /.test(c.t)));
const sidebar = page => page.evaluate(() =>
  [...new Set([...document.querySelectorAll("nav a[href], aside a[href], a[data-testid^=nav-]")]
    .filter(e => e.offsetParent !== null)
    .map(e => (e.innerText || "").replace(/\s+/g, " ").trim()).filter(Boolean))]);

test("mosaic_v2-payments-config", async ({ browser }) => {
  const jr = journal("mosaic_v2-payments-config");
  jr.target({ app: "owner+patron", ownerUrl: OWNER, patronUrl: PATRON, restaurantId: RID,
              table: TABLE, fixture: "Canary Mosaic V2 Electronic Pay — dev" });
  jr.note("_Can the owner of this restaurant see whether card payments are on, and choose which "
        + "methods are accepted? That is the half of this journey a person can check - the other "
        + "three steps are assertions about a JSON flag, a 409 and a Stripe client_secret._");
  jr.note("**Nothing is saved and nothing is paid.** These fixtures are live Stripe merchants on "
        + "dev, and toggling a shared fixture's kill switch would also hand the builder a false "
        + "red on the next canary run.");

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

  try {
    await jr.step("OWNER", "I sign in and look at what this restaurant's own menu offers me", async id => {
      const a = account("canaryOwner");
      await signInOwner(o, a.email, a.password, RID);
      await o.goto(`${OWNER}/owner/dashboard/?restaurantId=${RID}`, { waitUntil: "domcontentloaded" });
      await o.waitForTimeout(DWELL);
      const t = await look(o, jr, "owner", id, "dashboard for the Electronic Pay restaurant");
      rec.sidebar = await sidebar(o);
      jr.note(`> **This restaurant's sidebar:** ${rec.sidebar.join(" · ")}`);
      const hasPayments = rec.sidebar.some(x => /payment/i.test(x));
      return `The sidebar offers ${rec.sidebar.length} places to go: ${rec.sidebar.join(", ")}. `
           + `A Payments entry is ${hasPayments ? "THERE" : "NOT there — as this restaurant's owner I am never shown a way to reach payment settings"}. `
           + `Dashboard reads: ${t.slice(0, 140)}`;
    });

    await jr.step("OWNER", "I read the payments settings page — the baseline this journey starts from", async id => {
      const r = await o.goto(`${OWNER}/owner/settings/payments/?restaurantId=${RID}`, { waitUntil: "domcontentloaded" });
      await o.waitForTimeout(DWELL + 2000);
      const t = await look(o, jr, "owner", id, "payments settings, Electronic Pay");
      rec.baseline = { status: r && r.status(), text: t.slice(0, 900), controls: await realControls(o),
                       switcherHeader: await o.locator('[data-testid="restaurant-switcher"]').first().innerText().catch(() => "(none)") };
      jr.note(`> **The page, word for word:** "${rec.baseline.text.slice(0, 400)}"`);
      jr.note(`> **Every control on it that is not the sidebar:** `
            + (rec.baseline.controls.map(c => `\`${c.tid || c.type}\` "${c.t}"`).join(" · ") || "**none at all**"));
      return `HTTP ${rec.baseline.status}, header confirms "${rec.baseline.switcherHeader}". It says: "${rec.baseline.text.slice(0, 200)}". `
           + `Controls beyond the sidebar: ${rec.baseline.controls.length}.`;
    });

    await jr.step("OWNER", "I look for the accepted-methods control this journey round-trips", async id => {
      const methodish = rec.baseline.controls.filter(c => /card|apple|google|cash|method|accept|enable|toggle|switch|save/i.test(`${c.t} ${c.tid} ${c.type}`));
      rec.methods = methodish;
      await look(o, jr, "owner", id, "looking for accepted methods");
      if (!methodish.length)
        throw new Error("There is no control on this page for turning payments on, and none for choosing "
          + "which methods are accepted. The page is a single sentence and the sidebar. So the owner "
          + "round-trip this journey asserts is not something an owner can perform.");
      return `Offered: ${methodish.map(c => `"${c.t}"(${c.tid || c.type})`).join(", ")}`;
    });

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

    await jr.soft("OWNER", "Before I blame the page, I check it is not the same page for every restaurant", async id => {
      rec.across = {};
      for (const [slug, name] of [["mosaic_v2-electronic-pay", "Canary Mosaic V2 Electronic Pay — dev"],
                                  ["mosaic_v2-split", "Canary Mosaic V2 Split — dev"],
                                  ["canary-mosaic", "Canary Mosaic — dev"],
                                  ["micro-kitchen", "Canary Micro Kitchen — dev"]]) {
        await o.goto(`${OWNER}/owner/settings/payments/?restaurantId=${F[slug]}`, { waitUntil: "domcontentloaded" });
        await o.waitForTimeout(DWELL);
        const t = await mainText(o);
        rec.across[slug] = { header: await o.locator('[data-testid="restaurant-switcher"]').first().innerText().catch(() => "(none)"),
                             text: t.slice(0, 300), controls: (await realControls(o)).map(c => `${c.tid || c.type}:${c.t}`) };
        await look(o, jr, "owner", `${id}-${slug.replace(/\W+/g, "-")}`, `payments page for ${slug}`);
      }
      fs.writeFileSync(`${OUT}/w057-across.json`, JSON.stringify(rec.across, null, 1));
      jr.note("> **The same page on four restaurants:**\n>\n> - "
            + Object.entries(rec.across).map(([k, v]) => `**${k}** (header "${v.header}") → "${v.text.slice(0, 110)}" · controls: ${v.controls.join(", ") || "none"}`).join("\n> - "));
      const distinct = new Set(Object.values(rec.across).map(v => v.text.slice(0, 120)));
      return `The page reads ${distinct.size === 1 ? "IDENTICALLY on all four restaurants" : `differently across them (${distinct.size} distinct readings)`}, `
           + `and the header does follow the restaurant I chose.`;
    });

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