import { expect } from "e2e";
import { execFileSync, spawnSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";
import { test, WEB } from "./t3.ts";
import { APP_DIR, T3_HOME } from "../e2e.config.ts";

const git = (cwd: string, ...args: string[]) => execFileSync("git", args, { cwd, encoding: "utf8" }).trim();

test("Failed commit-message preparation leaves no blobs in either object store", WEB, async ({ t3, app, screen, browser }) => {
  const capture = async (label: string) => {
    await browser.evaluate(async () => {
      await Promise.all(document.getAnimations().map((animation) => animation.finished.catch(() => undefined)));
      return null;
    });
    let previous = await app.screenshot(`${label}-stability`);
    for (let attempt = 0; attempt < 5; attempt++) {
      await new Promise((resolve) => setTimeout(resolve, 500));
      const current = await app.screenshot(label);
      if (readFileSync(previous).equals(readFileSync(current))) return;
      previous = current;
    }
    throw new Error(`Capture did not settle: ${label}`);
  };
  const root = await t3.project("Object-store-proof", { "README.md": "Existing history must remain readable.\n" });
  git(root, "checkout", "-b", "proof-normal");
  const linked = `${root}-linked`;
  git(root, "worktree", "add", "-b", "proof-linked", linked);
  execFileSync(process.execPath, ["apps/server/src/bin.ts", "project", "add", linked, "--title", "Linked-worktree-proof", "--base-dir", T3_HOME], { cwd: APP_DIR });
  const results: number[] = [];
  for (const [name, cwd] of [["normal", root], ["linked", linked]]) {
    await t3.thread({ project: cwd, title: `${name} object-store proof`, messages: ["Start"] });
    const file = `never-staged-${name}.txt`;
    writeFileSync(`${cwd}/${file}`, `Unique unstaged blob for commit preparation proof: ${name}, 34595.\n`);
    const hash = git(cwd, "hash-object", file);
    const before = spawnSync("git", ["cat-file", "-e", hash], { cwd }).status;
    await t3.open();
    await screen.getByRole("button", `${name} object-store proof, Done, ${name === "normal" ? "Object-store-proof" : "Linked-worktree-proof"}`).tap();
    await screen.getByRole("button", "Git action options").tap();
    await browser.locator('[role="menuitem"]:not([data-disabled]):not([aria-disabled="true"])').filter({ hasText: /^Commit$/ }).tap();
    await screen.getByRole("dialog", "Commit changes").waitFor({ state: "visible" });
    await capture(`${name}-empty-message`);
    await screen.getByRole("dialog", "Commit changes").getByRole("button", "Commit", { exact: true }).tap();
    await screen.getByText("Action failed", { exact: true }).first().waitFor({ state: "visible" });
    await browser.evaluate(async () => {
      await Promise.all(document.getAnimations().map((animation) => animation.finished.catch(() => undefined)));
      return null;
    });
    const after = spawnSync("git", ["cat-file", "-e", hash], { cwd }).status;
    const history = git(cwd, "show", "HEAD:README.md");
    const count = git(cwd, "rev-list", "--count", "HEAD");
    const staged = git(cwd, "diff", "--cached", "--name-only");
    await browser.evaluate((data) => {
      document.getElementById("object-proof")?.remove();
      const panel = document.createElement("pre");
      panel.id = "object-proof";
      panel.style.cssText = "position:fixed;z-index:99999;left:280px;right:24px;bottom:24px;padding:24px;background:#111827;color:#f8fafc;border:2px solid #94a3b8;border-radius:12px;font:16px/1.7 monospace;white-space:pre-wrap";
      panel.textContent = data;
      document.body.append(panel);
    }, `${name === "normal" ? "Normal repository" : "Linked worktree / shared object store"} — measured Git output\n\nNew file: ${file}\n$ git hash-object ${file}\n${hash}\n$ git cat-file -e <hash>\nBefore preparation: exit ${before} (absent)\nAfter failed generation: exit ${after} (${after === 0 ? "OBJECT EXISTS — leaked blob" : "object absent — no leaked blob"})\n\nExisting HEAD:README.md: ${history}\nCommit count: ${count}; real index: ${staged || "empty"}`);
    await capture(`${name}-object-store`);
    await browser.evaluate(() => { document.getElementById("object-proof")?.remove(); });
    results.push(after ?? -1);
  }
  await expect(results).toEqual([1, 1]);
});
