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

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

test("Saved scheduled tasks recover from a deleted main base", { ...WEB, video: "off" }, async ({ t3, app, screen, browser }) => {
  await browser.addInitScript(() => {
    const redact = () => {
      const walker = document.createTreeWalker(document, NodeFilter.SHOW_TEXT);
      while (walker.nextNode()) {
        const node = walker.currentNode;
        const text = node.nodeValue ?? "";
        const safe = text.replace(/\/(?:home|tmp)\/[^\s)"<>]+/g, "[fixture workspace]");
        if (safe !== text) node.nodeValue = safe;
      }
    };
    new MutationObserver(redact).observe(document, { subtree: true, childList: true, characterData: true });
  });
  const remote = await t3.project("remote-master", { "README.md": "Remote master fallback fixture\n" });
  const local = await t3.project("local-beta", { "README.md": "Local beta fallback fixture\n" });
  git(remote, "branch", "-m", "master");
  const bare = `${remote}-origin.git`;
  git(remote, "clone", "--bare", remote, bare);
  git(remote, "remote", "add", "origin", bare);
  git(remote, "fetch", "origin");
  git(remote, "remote", "set-head", "origin", "-a");
  git(remote, "branch", "main");
  git(local, "branch", "beta");
  const cases = [
    { project: remote, title: "Remote master fallback", label: "remote-master", fallback: "master" },
    { project: local, title: "Local beta fallback", label: "local-beta", fallback: "beta" },
  ];
  const results: boolean[] = [];
  const state = makeState(T3_HOME);
  for (const item of cases) {
    await t3.open("/settings/scheduled-tasks");
    await screen.getByRole("button", "New task").waitFor();
    await app.startRecording({ name: item.label });
    await screen.getByRole("button", "New task").tap();
    await screen.getByLabel("Name").fill(item.title);
    await screen.getByLabel("Project").tap();
    await screen.getByRole("option", item.label).tap();
    await screen.getByLabel("Prompt").fill(`Show the workspace after ${item.label} fallback.`);
    await screen.getByRole("button", "Every interval").tap();
    await screen.getByRole("switch", "Enabled").tap();
    await app.screenshot(`${item.label}-saved-main`);
    await screen.getByRole("button", "Create task").tap();
    await screen.getByRole("dialog", "New task").waitFor({ state: "hidden" });
    git(item.project, "checkout", item.fallback);
    git(item.project, "branch", "-D", "main");
    const expectedTip = git(item.project, "rev-parse", "HEAD");
    await screen.getByRole("button", `Actions for ${item.title}`).tap();
    await screen.getByRole("menuitem", "Run now").tap();
    await screen.getByText(/^(running|failed|succeeded)$/).first().waitFor();
    const listed = await runPromise(state.cli(["thread", "list", "--project", item.project, "--json"]));
    const threads = JSON.parse(listed.stdout);
    const thread = threads.find((entry: { title: string }) => entry.title === item.title);
    await app.stopRecording();
    await t3.open({ threadId: thread.threadId });
    await screen.getByText(item.title).first().waitFor();
    await app.startRecording({ name: `${item.label}-result` });
    await screen.getByText(/Base ref "main" does not resolve|This turn is a replay\./, { exact: false }).first().waitFor({ timeout: 90_000 });
    const dismiss = screen.getByRole("button", "Dismiss error");
    if (await dismiss.first().isVisible()) await dismiss.first().tap();
    await dismiss.waitFor({ state: "hidden" });
    await app.screenshot(item.label);
    const reply = await screen.getByText("This turn is a replay.", { exact: false }).isVisible();
    const checkouts = git(item.project, "worktree", "list", "--porcelain");
    const tips = [...checkouts.matchAll(/^HEAD (.+)$/gm)].map((match) => match[1]);
    results.push(reply && tips.length === 2 && tips[1] === expectedTip);
    await app.stopRecording();
  }
  expect(results).toEqual([true, true]);
});
