import { mkdir, rename, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "e2e";
import { test, WEB } from "./t3.ts";

test("Recreated folders show current files", WEB, async ({ t3, app, screen, browser }) => {
  const project = await t3.project("Folder recreation", {
    "src/old.ts": "export const old = true;\n",
  });
  const thread = await t3.thread({ project, title: "Folder recreation", messages: ["Inspect workspace"] });

  // Schedule genuine RPC messages; all directory contents come from the real server and disk.
  await browser.addInitScript(() => {
    const Original = window.WebSocket;
    const decode = (data: any) => JSON.parse(typeof data === "string" ? data : new TextDecoder().decode(data));
    let childReady!: () => void;
    let rootReady!: () => void;
    const race = {
      armed: false,
      rootId: undefined as number | undefined,
      childId: undefined as number | undefined,
      releaseRoot: () => {},
      releaseChild: () => {},
      child: new Promise<void>((resolve) => { childReady = resolve; }),
      root: new Promise<void>((resolve) => { rootReady = resolve; }),
      order: [] as string[],
    };
    (window as any).directoryRace = race;
    window.WebSocket = class extends Original {
      addEventListener(type: string, listener: any, options?: any) {
        if (type !== "message") return super.addEventListener(type, listener, options);
        return super.addEventListener(type, (event: MessageEvent) => {
          const message = decode(event.data);
          const deliver = () => typeof listener === "function" ? listener.call(this, event) : listener.handleEvent(event);
          if (message._tag === "Exit" && message.requestId === race.childId) {
            race.order.push("Held real old-directory response");
            race.releaseChild = () => { race.childId = undefined; deliver(); race.order.push("Delivered old-directory response after removal"); };
            childReady();
            return;
          }
          deliver();
          if (message._tag === "Exit" && message.requestId === race.rootId) {
            race.order.push("Delivered real root response while src absent");
            rootReady();
          }
        }, options);
      }
      send(data: any) {
        const message = decode(data);
        if (race.armed && message._tag === "Request" && message.tag === "projects.listEntries") {
          if (message.payload.directoryPath === "" && race.rootId === undefined) {
            race.rootId = message.id;
            race.releaseRoot = () => super.send(data);
            race.order.push("Held root request until filesystem removal");
            return;
          }
          if (message.payload.directoryPath === "src") {
            race.childId = message.id;
            race.armed = false;
          }
        }
        super.send(data);
      }
    };
  });
  await t3.open(thread);
  await screen.getByRole("button", "Toggle right panel", { exact: true }).tap();
  await screen.getByRole("button", /^Files/).tap();
  const src = screen.getByRole("treeitem", "src", { exact: true });
  await src.tap();
  await screen.getByRole("treeitem", "old.ts", { exact: true }).waitFor();
  await app.screenshot("original-folder");

  await browser.evaluate(() => { (window as any).directoryRace.armed = true; });
  await screen.getByRole("button", "Refresh workspace files").tap();
  await browser.evaluate(() => (window as any).directoryRace.child);
  await rename(join(project, "src"), join(project, "..", "removed-original"));
  await browser.evaluate(() => { (window as any).directoryRace.releaseRoot(); });
  await browser.evaluate(() => (window as any).directoryRace.root);
  await src.waitFor({ state: "hidden" });
  await browser.evaluate(() => { (window as any).directoryRace.releaseChild(); });
  await app.screenshot("folder-removed");

  await mkdir(join(project, "src"));
  await writeFile(join(project, "src/new.ts"), "export const current = true;\n");
  await screen.getByRole("button", "Refresh workspace files").tap();
  await src.waitFor();
  if (await src.getAttribute("aria-expanded") !== "true") await src.tap();
  await screen.getByRole("treeitem", /(?:old|new)\.ts/).waitFor();
  await app.screenshot("recreated-folder");
  console.log("Filesystem race order:", await browser.evaluate(() => (window as any).directoryRace.order));
  await expect(screen.getByRole("treeitem", "new.ts", { exact: true })).toBeVisible();
  await expect(screen.getByRole("treeitem", "old.ts", { exact: true })).not.toBeVisible();
});
