import { expect } from "e2e";
import { spawn, execFileSync } from "node:child_process";
import { mkdirSync, writeFileSync, readFileSync, existsSync, createWriteStream } from "node:fs";
import { createServer } from "node:net";
import { once } from "node:events";
import { APP_DIR, T3_HOME } from "../e2e.config.ts";
import { test, WEB } from "./t3.ts";

const ROOT = "/tmp/factory-pr-31106-a1794362421-evidence-2";
const git = (cwd: string, ...args: string[]) => execFileSync("git", args, { cwd, encoding: "utf8" }).trim();
const cli = (...args: string[]) => execFileSync(process.execPath, ["apps/server/src/bin.ts", ...args], { cwd: APP_DIR, encoding: "utf8", env: { ...process.env, T3CODE_DEV_SEED: "0" } });

test("Occupied clone destination offers Cancel, Combine and Replace", WEB, async ({ t3, app, screen, browser }) => {
  const run = `${ROOT}/runs/${Date.now()}`;
  const home = `${run}/destination`;
  mkdirSync(`${home}/userdata`, { recursive: true });
  mkdirSync(`${home}/bin`, { recursive: true });
  writeFileSync(`${home}/bin/hostnamectl`, '#!/bin/sh\necho Destination\n', { mode: 0o755 });
  writeFileSync(`${home}/userdata/settings.json`, JSON.stringify({ providerInstances: { codex: { driver: "codex", enabled: false } } }));
  const source = `${ROOT}/fixtures/source`;
  const occupied = `${ROOT}/fixtures/occupied`;
  mkdirSync(source, { recursive: true });
  mkdirSync(occupied, { recursive: true });
  writeFileSync(`${source}/README.md`, "Repository README\n");
  writeFileSync(`${source}/missing.txt`, "Repository-only file\n");
  writeFileSync(`${source}/.gitignore`, ".env\n");
  writeFileSync(`${source}/.env`, "SYNTHETIC=source\n");
  if (!existsSync(`${source}/.git`)) {
    git(source, "init", "-b", "main"); git(source, "add", ".");
    git(source, "-c", "user.name=Scenario", "-c", "user.email=scenario@example.invalid", "commit", "-m", "Synthetic repository");
    git(source, "remote", "add", "origin", "https://example.invalid/scenario/clone-demo.git");
  }
  writeFileSync(`${occupied}/README.md`, "Locally modified README\n");
  writeFileSync(`${occupied}/local-only.txt`, "Keep local file\n");
  writeFileSync(`${occupied}/.env`, "SYNTHETIC=destination\n");
  cli("project", "add", source, "--title", "Clone demo", "--base-dir", T3_HOME);
  await t3.thread({ project: source, title: "Existing work", messages: ["Show the synthetic fixture"] });
  const net = createServer(); net.listen(0, "127.0.0.1"); await once(net, "listening");
  const port = (net.address() as { port: number }).port; await new Promise<void>(r => net.close(() => r()));
  const log = createWriteStream(`${run}/destination.log`);
  const child = spawn(process.execPath, ["scripts/dev-runner.ts", "dev:server", "--home-dir", home, "--no-seed", "--port", String(port)], {
    cwd: APP_DIR, detached: true, stdio: ["ignore", "pipe", "pipe"],
    env: { ...process.env, PATH: `${home}/bin:${process.env.PATH}`, T3CODE_DEV_SEED: "0", T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "0", T3CODE_DEV_ALLOWED_ORIGINS: app.baseUrl, GIT_CONFIG_COUNT: "1", GIT_CONFIG_KEY_0: `url.file://${source}.insteadOf`, GIT_CONFIG_VALUE_0: "https://example.invalid/scenario/clone-demo.git" },
  });
  child.stdout.pipe(log); child.stderr.pipe(log);
  try {
    await new Promise<void>((resolve, reject) => {
      const timeout = setTimeout(() => reject(new Error("Destination server did not start")), 120000);
      child.stdout.on("data", chunk => { if (String(chunk).includes("pairingUrl:")) { clearTimeout(timeout); resolve(); } });
      child.on("exit", code => { clearTimeout(timeout); reject(new Error(`Destination exited ${code}`)); });
    });
    const pairing = cli("auth", "pairing", "create", "--base-dir", home, "--base-url", `http://127.0.0.1:${port}`, "--ttl", "15m");
    const pairingUrl = /Pair URL:\s*(\S+)/.exec(pairing)?.[1];
    if (!pairingUrl) throw new Error("Pairing URL missing");
    await t3.open();
    await screen.getByRole("button", "Existing work,", { exact: false }).tap();
    await browser.evaluate(async (pairingUrl: string) => {
      const { connectPairing } = await import("/src/connection/onboarding.ts");
      const { appAtomRegistry } = await import("/src/rpc/atomRegistry.ts");
      const result = await connectPairing.run(appAtomRegistry, { pairingUrl });
      if (result._tag === "Failure") throw new Error("Destination pairing failed");
    }, pairingUrl);
    await screen.getByRole("button", "New thread", { exact: true }).tap();
    await screen.getByRole("button", "Run context:", { exact: false }).tap();
    await screen.getByRole("menuitem", "Add to another machine").tap();
    await screen.getByRole("menuitem", "Destination", { exact: true }).tap();
    await screen.getByLabel("Folder on Destination").fill(occupied);
    await app.screenshot("occupied-folder-ready");
    await screen.getByRole("button", "Clone to Destination").tap();
    // Capture whichever actual response arrived; the assertion is the regression condition.
    await browser.evaluate(async () => {
      await new Promise<void>((resolve, reject) => {
        const done = () => /This folder already contains files|Destination path already exists and is not empty/.test(document.body.innerText);
        if (done()) return resolve();
        const observer = new MutationObserver(() => { if (done()) { observer.disconnect(); clearTimeout(timeout); resolve(); } });
        const timeout = setTimeout(() => { observer.disconnect(); reject(new Error("No clone response")); }, 30000);
        observer.observe(document.body, { childList: true, subtree: true, characterData: true });
      });
    });
    await app.screenshot("occupied-folder-response");
    await expect(screen.getByRole("button", "Combine")).toBeVisible();
    await expect(screen.getByRole("button", "Replace")).toBeVisible();
    await screen.getByRole("button", "Cancel", { exact: true }).tap();
    expect(readFileSync(`${occupied}/README.md`, "utf8")).toBe("Locally modified README\n");
    expect(readFileSync(`${occupied}/local-only.txt`, "utf8")).toBe("Keep local file\n");
    expect(readFileSync(`${occupied}/.env`, "utf8")).toBe("SYNTHETIC=destination\n");
    expect(existsSync(`${occupied}/missing.txt`)).toBe(false);
    await app.screenshot("cancel-returned");
  } finally {
    if (child.pid) process.kill(-child.pid, "SIGTERM");
    log.end();
  }
});
