import { expect } from "e2e";
import { execFileSync, spawn } from "node:child_process";
import { randomBytes } from "node:crypto";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { createServer } from "node:http";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { setTimeout as delay } from "node:timers/promises";
import { test, WEB } from "./t3.ts";

// A real smart-HTTP Git remote, paced to keep the download visible on both commits.
test("New worktree separates checkout from a slow submodule download", WEB, async ({ t3, app, screen, browser }) => {
  const scratch = mkdtempSync(join(process.env.T3_EVIDENCE_SCRATCH ?? tmpdir(), "remote-"));
  const source = join(scratch, "source");
  mkdirSync(source);
  const git = (cwd: string, ...args: string[]) => execFileSync("git", args, { cwd, encoding: "utf8" }).trim();
  const commit = (cwd: string) => git(cwd, "-c", "user.name=Scenario", "-c", "user.email=scenario@example.invalid", "commit", "-qm", "Synthetic fixture");
  git(source, "init", "-q", "-b", "main");
  for (let i = 0; i < 128; i++) writeFileSync(join(source, `asset-${i}.bin`), randomBytes(64 * 1024));
  git(source, "add", ".");
  commit(source);
  const revision = git(source, "rev-parse", "HEAD");
  git(scratch, "clone", "-q", "--bare", source, "assets.git");
  let downloaded!: () => void;
  const downloadVisible = new Promise<void>((resolve) => { downloaded = resolve; });
  const server = createServer(async (req, res) => {
    const url = new URL(req.url!, "http://127.0.0.1");
    const backend = spawn("git", ["http-backend"], { env: {
      ...process.env, GIT_PROJECT_ROOT: scratch, GIT_HTTP_EXPORT_ALL: "1",
      PATH_INFO: url.pathname, QUERY_STRING: url.search.slice(1),
      REQUEST_METHOD: req.method, CONTENT_TYPE: req.headers["content-type"] ?? "",
      CONTENT_LENGTH: req.headers["content-length"] ?? "", SERVER_PROTOCOL: "HTTP/1.1",
    }});
    req.pipe(backend.stdin);
    const chunks: Buffer[] = [];
    for await (const chunk of backend.stdout) chunks.push(Buffer.from(chunk));
    const output = Buffer.concat(chunks);
    const boundary = output.indexOf("\r\n\r\n");
    const headers = output.subarray(0, boundary).toString();
    for (const line of headers.split("\r\n")) {
      const colon = line.indexOf(":");
      if (colon > 0 && !line.startsWith("Status:")) res.setHeader(line.slice(0, colon), line.slice(colon + 1).trim());
    }
    const body = output.subarray(boundary + 4);
    if (req.method === "POST") {
      for (let offset = 0; offset < body.length; offset += 32768) {
        if (res.destroyed) break;
        res.write(body.subarray(offset, offset + 32768));
        if (offset >= 2 * 1024 * 1024) downloaded();
        await delay(125);
      }
      res.end();
    } else res.end(body);
  });
  await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
  try {
    const address = server.address() as { port: number };
    const project = await t3.project("Submodule demo", {
      "t3.json": JSON.stringify({ defaultThreadEnvMode: "worktree", worktreeSubmodules: "recursive", scripts: [{ name: "Prepare workspace", command: "sleep 8", runOnWorktreeCreate: true, async: false }] }),
      ".gitmodules": `[submodule "assets"]\n\tpath = assets\n\turl = http://127.0.0.1:${address.port}/assets.git\n`,
      ...Object.fromEntries(Array.from({length: 600}, (_, i) => [`src/file-${i}.txt`, "Synthetic checkout file\n"])),
    });
    git(project, "update-index", "--add", "--cacheinfo", `160000,${revision},assets`);
    commit(project);
    await t3.open();
    await screen.getByRole("button", "Toggle thread details panel").tap();
    await screen.getByRole("combobox", "Workspace").tap();
    await screen.getByRole("option", "New worktree").tap();
    await screen.getByTestId("composer-editor").fill("Inspect this project after worktree setup");
    await screen.getByRole("button", "Submit message").tap();
    await app.screenshot("launch-requested");
    await Promise.race([downloadVisible, delay(30_000).then(() => { throw new Error("The UI did not start the synthetic submodule download"); })]);
    await app.screenshot("submodule-downloading");
    await expect(screen.getByText("Init submodules", { exact: true }).first()).toBeVisible();
    await expect(screen.getByText("Receiving objects:", { exact: false }).first()).toBeVisible();
    await expect(browser.locator('[data-worktree-setup-stage="submodules"]')).toHaveAttribute("data-worktree-setup-status", "done", { timeout: 90_000 });
    await expect(browser.locator('[data-worktree-setup-stage="checkout"]')).toHaveAttribute("data-worktree-setup-status", "done");
    await app.screenshot("submodules-complete");
    await screen.getByText("Worked for", { exact: false }).first().waitFor({ state: "visible", timeout: 90_000 });
  } finally {
    server.closeAllConnections();
    await new Promise<void>((resolve) => server.close(() => resolve()));
  }
});
