/* WITNESS · menu-subcat-order  (W-072 mosaic_v2-subcat-order)
 *
 * THE PROMISE (step 10 of the journey, in its own words): "Owner UI: drag-reorder a
 * sub-category row → diner sort_order changes", and step 11: "Patron UI Reflects Owner-UI
 * Drag Reorder". That is the one step of this journey that is explicitly about a screen a
 * person uses, so it is the one I go looking for.
 *
 * I open the owner's Menu builder and try to find a sub-category row I could drag. Then I
 * sit down as a diner and write down the order the sub-category rail is actually in.
 * I DO NOT create or delete sub-categories on a live canary menu.
 */
import { test } from "@playwright/test";
import { journal, look, arriveAsDiner, openCourse, roleContext, deskContext, closeRole,
         signInOwner, account, OWNER, PATRON, DWELL } from "../lib-witness.mjs";
import fs from "node:fs";

const RID = "28ae37ab-706c-4e20-83d7-52f5d2dff747";   // Canary Mosaic — dev
const TABLE = 9972;

test("menu-subcat-order", async ({ browser }) => {
  test.setTimeout(28 * 60_000);
  const jr = journal("menu-subcat-order");
  jr.target({ ownerApp: OWNER, patronApp: PATRON, restaurantId: RID, fixture: "canary-mosaic",
              table: TABLE, route: "/owner/menu/ (menu builder)" });
  jr.note("**Read only.** I look for the drag handle; I do not create, delete or reorder "
        + "anything on a live canary menu.");

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

  try {
    await jr.step("OWNER", "I open my menu builder and look for my sub-categories", async id => {
      const a = account("canaryOwner");
      await signInOwner(owner, a.email, a.password, RID);
      await owner.goto(`${OWNER}/owner/menu/?restaurantId=${RID}`, { waitUntil: "domcontentloaded" });
      await owner.waitForTimeout(DWELL * 2);
      // Open every course bucket, or "no sub-category rows" is just a collapsed accordion.
      await owner.locator('[data-testid="collapse-expand-all-btn"]').first()
                 .click({ timeout: 15000 }).catch(() => {});
      await owner.waitForTimeout(3500);
      const t = await look(owner, jr, "owner", id, "menu builder, everything expanded");
      state.owner = await owner.evaluate(() => {
        const vis = e => e.offsetParent !== null;
        const ids = [...document.querySelectorAll("[data-testid]")].filter(vis)
          .map(e => e.getAttribute("data-testid"));
        return {
          subcatIds: [...new Set(ids.filter(t => /subcat/i.test(t)))].sort(),
          dragIds:   [...new Set(ids.filter(t => /drag|handle|reorder|sortable|grip/i.test(t)))].sort(),
          draggables: document.querySelectorAll('[draggable="true"]').length,
          buckets: [...document.querySelectorAll('[data-testid^="category-bucket-"]')]
            .map(e => (e.innerText||"").replace(/\s+/g," ").trim().slice(0,90)),
        };
      });
      fs.writeFileSync(`${jr.dir}/owner-menu.txt`, t);
      jr.note(`> **Sub-category controls on my menu builder:** `
            + `${state.owner.subcatIds.join(" · ") || "none"}`);
      jr.note(`> **Anything draggable:** ${state.owner.dragIds.join(" · ") || "no drag testid"} · `
            + `\`[draggable=true]\` elements: **${state.owner.draggables}**`);
      return `subcat controls: ${state.owner.subcatIds.join(", ") || "none"} · `
           + `draggable elements: ${state.owner.draggables}`;
    });

    await jr.soft("OWNER", "There is a sub-category ROW I could drag to reorder", async id => {
      const rows = state.owner.subcatIds.filter(t => !/create-add/i.test(t));
      await look(owner, jr, "owner", id, "looking for a sub-category row to drag");
      if (!rows.length)
        throw new Error(
          "The only sub-category control on my menu builder is `subcategory-create-add-<course>` "
        + "— an ADD button. There is not one existing sub-category row on this menu, so there "
        + "is nothing here to drag. The step this journey calls \"Owner UI: drag-reorder a "
        + "sub-category row\" has no row to act on unless I first create sub-categories on a "
        + "live canary menu, which I will not do.");
      return `sub-category rows present: ${rows.join(", ")}`;
    });

    await jr.step("PATRON", "As a diner, what order is the sub-category rail in?", async id => {
      await arriveAsDiner(diner, RID, TABLE, "mosaic_v2");
      const readRail = () => diner.evaluate(() => {
        const nav = document.querySelector('[data-testid="m2-subcat-nav"]');
        return {
          rail: (nav?.innerText || "").replace(/\s+/g, " ").trim(),
          jumps: [...document.querySelectorAll('[data-testid^="m2-subcat-jump-"]')]
            .map(e => `${e.getAttribute("data-testid")}="${(e.innerText||"").replace(/\s+/g," ").trim()}"`),
        };
      });
      await look(diner, jr, "patron", id, "the rail on Drinks");
      state.drinks = await readRail();
      await openCourse(diner, "Entrees");
      await look(diner, jr, "patron", id + "b", "the rail on Mains");
      state.mains = await readRail();
      jr.note(`> **Rail on Drinks:** \`${state.drinks.rail}\` — ${state.drinks.jumps.join(" · ")}`);
      jr.note(`> **Rail on Mains:** \`${state.mains.rail}\` — ${state.mains.jumps.join(" · ")}`);
      return `Drinks "${state.drinks.rail}" · Mains "${state.mains.rail}"`;
    });

    await jr.soft("PATRON", "The rail is showing me real sub-categories, not just raw courses",
      async id => {
        const named = [...state.drinks.jumps, ...state.mains.jumps]
          .filter(j => !/-more=/.test(j));
        if (!named.length) throw new Error("the rail offers nothing but a MORE bucket");
        jr.note(`> **Named rail entries:** ${named.join(" · ")} — these are the course's own raw `
              + `categories (Beverages, Sides), not sub-categories an owner filed.`);
        return named.join(", ");
      });

    verdict = "WALKED"; why = "Looked for the drag handle and wrote down the diner's rail order.";
  } catch (e) {
    verdict = "STOPPED"; why = String(e.message||e).split("\n")[0].slice(0,300);
  } finally {
    fs.writeFileSync(`${jr.dir}/state.json`, JSON.stringify(state, null, 1));
    jr.finish(verdict, why);
    await closeRole(pc, jr, "patron"); await closeRole(oc, jr, "owner");
  }
});
