import { expect } from "e2e";
import { readdir, rm, access } from "node:fs/promises";
import { execFileSync } from "node:child_process";
import { test, WEB, makeState, runPromise } from "./t3.ts";
import { T3_HOME } from "../e2e.config.ts";

test("A resumed turn restores a worktree containing only dependencies", WEB, async ({ t3, app, screen, browser }) => {
  const project = await t3.project("checkout-demo", {
    "package.json": JSON.stringify({ name: "checkout-demo", private: true, version: "1.0.0", dependencies: { "is-number": "7.0.0" } }),
    "pnpm-lock.yaml": "lockfileVersion: '9.0'\nsettings:\n  autoInstallPeers: true\n  excludeLinksFromLockfile: false\nimporters:\n  .:\n    dependencies:\n      is-number:\n        specifier: 7.0.0\n        version: 7.0.0\npackages:\n  is-number@7.0.0:\n    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}\nsnapshots:\n  is-number@7.0.0: {}\n",
    "src/index.js": "console.log('tracked project file');\n"
  });
  const { cli } = makeState(T3_HOME);
  const call = async (args: string[]) => (await runPromise(cli(args))).stdout;
  const started = JSON.parse(await call(["thread", "start", project, "Start a worktree turn", "--worktree", "main", "--branch", "proof/recover-checkout", "--title", "Worktree checkout recovery", "--provider", "devReplay", "--model", "multi-turn", "--wait", "--json"]));
  const thread = { threadId: started.threadId };
  const state = JSON.parse(await call(["thread", "show", thread.threadId, "--json"]));
  const folder = state.thread.worktreePath;
  execFileSync("pnpm", ["install", "--no-frozen-lockfile"], { cwd: folder, stdio: "pipe" });
  const branch = execFileSync("git", ["branch", "--show-current"], { cwd: folder, encoding: "utf8" }).trim();
  // Simulate storage cleanup leaving dependency folders after the turn has stopped.
  for (const entry of await readdir(folder)) {
    if (entry !== "node_modules") await rm(`${folder}/${entry}`, { recursive: true, force: true });
  }
  execFileSync("git", ["worktree", "prune"], { cwd: project });
  await call(["thread", "send", thread.threadId, "Report current directory, branch, and tracked files after checkout recovery.", "--wait", "--json"]);
  let restored = false;
  try { await access(`${folder}/.git`); restored = true; } catch {}
  const currentBranch = restored ? execFileSync("git", ["branch", "--show-current"], { cwd: folder, encoding: "utf8" }).trim() : "MISSING CHECKOUT";
  const tracked = restored ? execFileSync("git", ["ls-files"], { cwd: folder, encoding: "utf8" }).trim() : "NONE — .git is absent";
  const diagnostic = `Observed filesystem after the resumed turn\nExpected branch: ${branch}\nCurrent branch: ${currentBranch}\nTracked files:\n${tracked}\nRemaining entries: ${(await readdir(folder)).join(", ")}\nCheckout restored: ${restored ? "YES" : "NO"}`;
  await call(["thread", "send", thread.threadId, diagnostic, "--wait", "--json"]);
  await t3.open(thread);
  await screen.getByRole("button", "Agent Observed filesystem after the resumed turn").tap();
  await app.screenshot("resumed-worktree-checkout");
  const cleanupStarted = JSON.parse(await call(["thread", "start", project, "Prepare archived worktree cleanup", "--worktree", "main", "--branch", "proof/cleanup-folder", "--title", "Archived cleanup candidate", "--provider", "devReplay", "--model", "multi-turn", "--wait", "--json"]));
  const cleanupState = JSON.parse(await call(["thread", "show", cleanupStarted.threadId, "--json"]));
  const cleanupFolder = cleanupState.thread.worktreePath;
  execFileSync("pnpm", ["install", "--no-frozen-lockfile"], { cwd: cleanupFolder, stdio: "pipe" });
  for (const entry of await readdir(cleanupFolder)) {
    if (entry !== "node_modules") await rm(`${cleanupFolder}/${entry}`, { recursive: true, force: true });
  }
  execFileSync("git", ["worktree", "prune"], { cwd: project });
  const environment = await browser.evaluate(() => location.pathname.split("/")[1]);
  await app.open(`/${environment}/${cleanupStarted.threadId}`);
  await screen.getByRole("button", "Thread actions for Archived cleanup candidate").tap();
  await screen.getByRole("menuitem", "Archive thread").tap();
  await app.open("/settings/worktrees");
  await screen.getByRole("button", "Advanced").last().tap();
  await screen.getByRole("switch", "Delete unchanged worktrees").tap();
  await app.open("/settings/maintenance");
  await screen.getByRole("button", "Delete now", { exact: true }).tap();
  await browser.evaluate(() => new Promise<void>((resolve) => {
    const ready = () => [...document.querySelectorAll("button")].some((button) => button.textContent?.trim() === "Delete now" && !button.disabled);
    if (ready()) return resolve();
    const observer = new MutationObserver(() => { if (ready()) { observer.disconnect(); resolve(); } });
    observer.observe(document.body, { subtree: true, attributes: true, childList: true, characterData: true });
  }));
  await browser.locator("#storage-cleanup-last-run").scrollIntoView();
  await app.screenshot("archived-folder-cleanup");
  expect(restored).toBe(true);
  expect(currentBranch).toBe(branch);
});
