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

test("Reftable cherry-picks and reverts refuse selected files and accept all files", WEB, async ({ t3, app, screen, browser }) => {
  const executable = process.env.SCENARIO_GIT_BIN;
  if (!executable) throw new Error("SCENARIO_GIT_BIN must point to a Git with reftable support");
  symlinkSync(executable, `${T3_HOME}/bin/git`);
  const results: unknown[] = [];
  const expected: unknown[] = [];
  for (const operation of ["cherry-pick", "revert"] as const) {
    const project = await t3.project(`Reftable ${operation}`, { "README.md": "base\n" });
    const git = (...args: string[]) => execFileSync(executable, args, { cwd: project, encoding: "utf8" });
    git("refs", "migrate", "--ref-format=reftable");
    git("config", "user.name", "Scenario");
    git("config", "user.email", "scenario@example.invalid");
    if (operation === "cherry-pick") git("checkout", "-b", "other");
    writeFileSync(`${project}/README.md`, "other\n");
    writeFileSync(`${project}/first.txt`, "first\n");
    git("add", "."); git("-c", "user.name=Original Author", "-c", "user.email=author@example.invalid", "commit", "-m", "first");
    const target = git("rev-parse", "HEAD").trim();
    if (operation === "cherry-pick") git("checkout", "main");
    writeFileSync(`${project}/README.md`, "mainchange\n");
    git("add", "."); git("commit", "-m", "mainchange");
    try { git(operation, target); } catch { /* expected README conflict */ }
    writeFileSync(`${project}/README.md`, "resolved\n");
    git("add", "README.md");
    const ref = operation === "cherry-pick" ? "CHERRY_PICK_HEAD" : "REVERT_HEAD";
    if (existsSync(`${project}/.git/${ref}`) || git("rev-parse", "--show-ref-format").trim() !== "reftable") throw new Error("Fixture must store operation state in reftable, with no loose operation file");
    const state = () => {
      let operationHead = "";
      try { operationHead = git("rev-parse", "--verify", ref); } catch { /* operation finished */ }
      return { head: git("rev-parse", "HEAD"), index: readFileSync(`${project}/.git/index`).toString("base64"), operationHead };
    };
    const before = state();
    if (!before.operationHead) throw new Error("Fixture did not start its operation");
    const thread = await t3.thread({ project, title: `Selected files during ${operation}`, messages: ["first"] });
    await t3.open(thread);
    await browser.evaluate((repositoryPath) => {
      const redact = () => {
        const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
        for (let node = walker.nextNode(); node; node = walker.nextNode()) {
          if (node.textContent?.includes(repositoryPath)) node.textContent = node.textContent.replaceAll(repositoryPath, "[repository path redacted]");
        }
      };
      new MutationObserver(redact).observe(document.body, { childList: true, subtree: true, characterData: true });
    }, project);
    const commit = async (message: string, selected: boolean) => {
      await screen.getByRole("button", "Git action options").tap();
      await screen.getByRole("menuitem", "Commit", { exact: true }).tap();
      if (selected) {
        await screen.getByRole("button", "Edit", { exact: true }).tap();
        await screen.getByRole("checkbox").nth(2).tap();
      }
      await screen.getByPlaceholder("Leave empty to auto-generate").fill(message);
      await app.screenshot(`${operation}-${selected ? "selected-files" : "all-files"}`);
      await screen.getByRole("button", "Commit", { exact: true }).tap();
      await browser.evaluate(() => new Promise<void>((resolve, reject) => {
        const done = () => /Action failed|Committed [0-9a-f]{7}/.test(document.body.innerText);
        if (done()) return resolve();
        const observer = new MutationObserver(() => { if (done()) { observer.disconnect(); resolve(); } });
        observer.observe(document.body, { childList: true, subtree: true, characterData: true });
        setTimeout(() => { observer.disconnect(); reject(new Error("Commit result did not appear")); }, 30000);
      }));
      await browser.evaluate(() => new Promise(resolve => setTimeout(resolve, 1000)));
      await app.screenshot(`${operation}-${selected ? "selected-result" : "all-result"}`);
    };
    await commit("Commit first.txt only", true);
    results.push(state()); expected.push(before);
    // Reload to clear the previous result before committing all remaining files.
    await t3.open(thread);
    await commit(`Finish ${operation} with all files`, false);
    const afterAll = state();
    results.push({ operationHead: afterAll.operationHead, author: operation === "cherry-pick" ? git("log", "-1", "--format=%an").trim() : "Scenario" });
    expected.push({ operationHead: "", author: operation === "cherry-pick" ? "Original Author" : "Scenario" });
  }
  await expect(results).toEqual(expected);
});
