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

const pause = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

test("Automatic pull finishes a checkout with a 35-second required smudge filter", WEB,
  async ({ t3, app, screen, browser }) => {
    const cwd = await t3.project("Slow checkout", {
      "a-plain.txt": "plain-v1\n",
      "z-slow.txt": "slow-v1\n",
      ".gitattributes": "z-slow.txt filter=slow\n",
    });
    const fixture = join(cwd, ".git", "slow-fixture");
    mkdirSync(fixture);
    const commitEnv = { ...process.env, GIT_AUTHOR_DATE: "2025-01-01T00:00:00Z", GIT_COMMITTER_DATE: "2025-01-01T00:00:00Z" };
    const git = (...args: string[]) => execFileSync("git", args, { cwd, encoding: "utf8", env: commitEnv }).trim();
    git("-c", "user.name=Scenario", "-c", "user.email=scenario@example.invalid", "commit", "--amend", "--no-edit", "--date=2025-01-01T00:00:00Z");
    git("init", "--bare", "--initial-branch=main", join(fixture, "remote.git"));
    git("remote", "add", "origin", join(fixture, "remote.git"));
    git("push", "-u", "origin", "main");
    git("remote", "set-head", "origin", "main");
    const writer = join(fixture, "writer");
    git("clone", join(fixture, "remote.git"), writer);
    writeFileSync(join(writer, "a-plain.txt"), "plain-v2\n");
    writeFileSync(join(writer, "z-slow.txt"), "slow-v2\n");
    const upstream = (...args: string[]) => execFileSync("git", args, { cwd: writer, encoding: "utf8", env: commitEnv }).trim();
    upstream("add", ".");
    upstream("-c", "user.name=Scenario", "-c", "user.email=scenario@example.invalid", "commit", "-m", "Update both files");
    const target = upstream("rev-parse", "HEAD");
    const marker = join(fixture, "smudge-started");
    const finished = join(fixture, "smudge-finished");
    const filter = join(fixture, "smudge.sh");
    writeFileSync(filter, `#!/bin/sh\ntouch '${marker}'\nsleep 35\ncat\ntouch '${finished}'\n`);
    git("config", "filter.slow.smudge", `sh '${filter}'`);
    git("config", "filter.slow.clean", "cat");
    git("config", "filter.slow.required", "true");
    writeFileSync(join(fixture, "report.sh"), `#!/bin/sh
printf '\u001b[2J\u001b[H'
echo 'AUTOMATIC PULL / 35-second required smudge filter'
echo 'Repository state after the slow checkout window:'
printf 'HEAD:     '; git rev-parse --short HEAD
printf 'Upstream: '; git rev-parse --short origin/main
if [ "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" ]; then echo 'HEAD matches upstream'; else echo 'HEAD DID NOT MOVE TO UPSTREAM'; fi
echo 'Working tree (git status --short):'
status=$(git status --short); if [ -z "$status" ]; then echo '  clean'; else printf '%s\\n' "$status"; fi
printf 'a-plain.txt: '; cat a-plain.txt
printf 'z-slow.txt:  '; if [ -f z-slow.txt ]; then cat z-slow.txt; else echo MISSING; fi
echo 'Next pull attempt (git pull --ff-only):'
git pull --ff-only 2>&1
touch .git/slow-fixture/report-done
`);
    await t3.thread({ project: cwd, title: "Slow checkout reproduction", messages: ["Ready"] });
    console.log("Opening app");
    await t3.open();
    await screen.getByTestId("sidebar-row-card").tap({ timeout: 120_000 });
    console.log("Opening source control settings");
    await screen.getByRole("button", "Settings").tap();
    await screen.getByRole("button", "Source control").tap();
    await screen.getByRole("switch", "Default automatic pull").tap({ timeout: 120_000 });
    const savedAutoPull = () => JSON.parse(readFileSync(join(T3_HOME, "userdata/settings.json"), "utf8")).defaultAutoPull === true;
    for (let i = 0; i < 40 && !savedAutoPull(); i++) await pause(250);
    if (!savedAutoPull()) throw new Error("Automatic pull setting was not persisted");
    console.log("Automatic pull enabled and persisted");
    await screen.getByRole("button", "Close", { exact: true }).tap();
    upstream("push");
    git("fetch", "origin");
    await screen.getByRole("group", "Git actions").hover({ timeout: 120_000 });
    const options = screen.getByRole("button", "Git action options");
    if (!(await options.isDisabled())) {
      await options.tap();
      await browser.keyboard.press("Escape");
    }
    for (let i = 0; i < 180 && !existsSync(marker); i++) await pause(500);
    if (!existsSync(marker)) throw new Error("Automatic pull never reached the smudge filter");
    await app.screenshot("checkout-in-progress");
    // Use the real wall clock: the filter must outlast the former 30-second deadline.
    await pause(40_000);
    console.log("CHECKOUT RESULT", JSON.stringify({ head: git("rev-parse", "HEAD"), target, status: git("status", "--short"), plain: readFileSync(join(cwd, "a-plain.txt"), "utf8"), slow: existsSync(join(cwd, "z-slow.txt")) ? readFileSync(join(cwd, "z-slow.txt"), "utf8") : "MISSING", filterFinished: existsSync(finished) }));
    await screen.getByRole("button", "Toggle terminal drawer").tap();
    await pause(1500);
    const drawer = await browser.locator("[data-thread-terminal-drawer]").boundingBox();
    if (drawer) {
      await browser.mouse.move(drawer.x + drawer.width / 2, drawer.y + 3);
      await browser.mouse.down();
      await browser.mouse.move(drawer.x + drawer.width / 2, drawer.y - 220);
      await browser.mouse.up();
    }
    await browser.keyboard.type("export PS1='$ '; sh .git/slow-fixture/report.sh");
    await browser.keyboard.press("Enter");
    for (let i = 0; i < 40 && !existsSync(join(fixture, "report-done")); i++) await pause(250);
    if (!existsSync(join(fixture, "report-done"))) throw new Error("The integrated terminal did not run the repository report");
    await pause(1000);
    await app.screenshot("checkout-result");
    expect(git("rev-parse", "HEAD")).toBe(target);
    expect(git("status", "--short")).toBe("");
    expect(readFileSync(join(cwd, "a-plain.txt"), "utf8")).toBe("plain-v2\n");
    expect(readFileSync(join(cwd, "z-slow.txt"), "utf8")).toBe("slow-v2\n");
  });
