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

test("Explicit worktree branches are refused before creation, retries replay, and provisioning errors are classified", WEB, async ({ t3, app, browser, screen }) => {
  const repo = await t3.project("Branch namespace demo", { "README.md": "Synthetic branch launch demonstration.\n" });
  const git = (...args: string[]) => execFileSync("git", args, { cwd: repo, encoding: "utf8" });
  git("branch", "werkstatt");
  git("branch", "foo/bar");
  git("branch", "replay-base");
  await t3.open();
  await screen.getByRole("textbox").fill("Branch launch demonstration");
  await browser.evaluate(async () => {
    const entities = await import("/src/state/entities.ts");
    const threads = await import("/src/state/threads.ts");
    const { appAtomRegistry: registry } = await import("/src/rpc/atomRegistry.ts");
    const { vcsEnvironment } = await import("/src/state/vcs.ts");
    const project = entities.readProjects().find((p) => p.title === "Branch namespace demo")!;
    const modelSelection = { instanceId: "devReplay", model: "multi-turn" };
    const requests = new Map();
    const acceptedIds = new Set();
    const rows: string[] = [];
    const errors = (value: unknown) => {
      const found: any[] = [];
      const visit = (v: any) => {
        if (!v || typeof v !== "object") return;
        if (v._tag === "OrchestrationV2ThreadLaunchError" || v._tag === "GitCommandError") found.push(v);
        for (const child of Object.values(v)) visit(child);
      };
      visit(value);
      return found;
    };
    const clean = (text: string) => text.replaceAll(project.workspaceRoot, "[repository]");
    const show = (heading = "REAL CLIENT RPC — DRIVER TRANSCRIPT", from = 0, to = rows.length) => {
      let panel = document.getElementById("proof-transcript");
      if (!panel) {
        panel = document.createElement("section");
        panel.id = "proof-transcript";
        panel.style.cssText = "position:fixed;z-index:9999;left:290px;right:24px;top:65px;bottom:24px;padding:24px;background:#14171d;color:#e5e7eb;border:1px solid #64748b;border-radius:12px;overflow:auto;font:15px/1.5 monospace;white-space:pre-wrap";
        document.body.append(panel);
      }
      panel.textContent = heading + "\nBrowser client command → WebSocket → real server\nSynthetic repository • base main • existing werkstatt and foo/bar\n\n" + rows.slice(from, to).join("\n\n");
    };
    const shell = () => registry.get(threads.environmentThreadShells.threadShellsAtom).filter((t) => t.projectId === project.id);
    const launch = async (branch: string, baseRef = "main", key = branch, replay = false) => {
      let input = requests.get(key);
      if (!input || !replay) {
        input = {
          commandId: crypto.randomUUID(), threadId: crypto.randomUUID(),
          message: { messageId: crypto.randomUUID(), role: "user", text: "Give me a short tour", attachments: [] },
          modelSelection, runtimeMode: "approval-required", interactionMode: "default",
          bootstrap: {
            createThread: { projectId: project.id, title: branch, modelSelection, runtimeMode: "approval-required", interactionMode: "default", branch: null, worktreePath: null, createdAt: new Date().toISOString() },
            prepareWorktree: { projectCwd: project.workspaceRoot, baseBranch: baseRef, branch },
          },
        };
        requests.set(key, input);
      }
      const before = shell().length;
      const result = await threads.threadEnvironment.startTurn.run(registry, { environmentId: project.environmentId, input });
      if (result._tag === "Success") acceptedIds.add(input.threadId);
      const error = errors(result)[0];
      const data = { accepted: result._tag === "Success", message: error?.message ?? "", resumed: result._tag === "Success" ? result.value.resumed : false, threadId: input.threadId, before };
      rows.push(`${replay ? "REPLAY accepted command" : "LAUNCH"} ${branch} (base ${baseRef})\n${data.accepted ? "ACCEPTED" : "REFUSED"}${replay ? ` • resumed=${data.resumed}` : ""}${data.message ? "\n" + clean(data.message) : ""}`);
      show();
      return data;
    };
    const provision = async (branch: string) => {
      const result = await vcsEnvironment.createWorktree.run(registry, { environmentId: project.environmentId, input: { cwd: project.workspaceRoot, refName: "main", newRefName: branch, path: null } });
      const error = errors(result).find((e) => e._tag === "GitCommandError");
      const serialized = JSON.stringify(result);
      const data = { detail: error?.detail ?? "", reason: error?.reason ?? "unclassified", command: error?.command ?? "", rawStderrExposed: /fatal:|cannot lock ref|refs\/heads\//.test(serialized) };
      rows.push(`PROVISION ${branch} — vcs.createWorktree\n${clean(data.detail)} • command=${data.command} • reason=${data.reason}\nRaw git stderr exposed: ${data.rawStderrExposed ? "yes" : "no"}`);
      show();
      return data;
    };
    (window as any).branchProof = { launch, provision, rows, show, shell, requests, project, acceptedIds };
  });
  const nested = await browser.evaluate(() => (window as any).branchProof.launch("werkstatt/baseline"));
  const reverse = await browser.evaluate(() => (window as any).branchProof.launch("foo"));
  const missing = await browser.evaluate(() => (window as any).branchProof.launch("missing-base-control", "missing-ref"));
  const provisionNested = await browser.evaluate(() => (window as any).branchProof.provision("werkstatt/provision"));
  const provisionReverse = await browser.evaluate(() => (window as any).branchProof.provision("foo"));
  const flat = await browser.evaluate(() => (window as any).branchProof.launch("werkstatt-baseline", "replay-base", "control"));
  // Wait for the real replay turn and workspace preparation before testing its accepted receipt.
  await expect.poll(async () => browser.evaluate(() => {
    const p = (window as any).branchProof;
    return p.shell().find((t) => t.id === p.requests.get("control").threadId)?.runtime?.status ?? "absent";
  }), { timeout: 120000 }).toMatch(/idle|completed/);
  git("branch", "-m", "werkstatt-baseline", "retired-control");
  git("branch", "werkstatt-baseline/child");
  git("branch", "-D", "replay-base");
  const replay = await browser.evaluate(() => (window as any).branchProof.launch("werkstatt-baseline", "replay-base", "control", true));
  const freshBlocked = await browser.evaluate(() => (window as any).branchProof.launch("werkstatt-baseline", "main", "fresh-blocked"));
  await browser.evaluate(async () => {
    const p = (window as any).branchProof;
    const { appAtomRegistry: registry } = await import("/src/rpc/atomRegistry.ts");
    const { environmentThreadShells } = await import("/src/state/threads.ts");
    const stopped = () => p.shell().length === p.acceptedIds.size && p.shell().every((t) => ["idle", "completed", "failed"].includes(t.runtime?.status));
    if (!stopped()) await new Promise<void>((resolve) => {
      const unsubscribe = registry.subscribe(environmentThreadShells.threadShellsAtom, () => {
        if (stopped()) { unsubscribe(); resolve(); }
      });
      if (stopped()) { unsubscribe(); resolve(); }
    });
    const style = document.createElement("style");
    style.textContent = "*,*::before,*::after{animation:none!important;transition:none!important}";
    document.head.append(style);
  });
  const summary = await browser.evaluate(() => {
    const p = (window as any).branchProof;
    const shells = p.shell();
    const count = (key: string) => shells.filter((t) => t.id === p.requests.get(key).threadId).length;
    const result = { nested: count("werkstatt/baseline"), reverse: count("foo"), missing: count("missing-base-control"), control: count("control"), fresh: count("fresh-blocked") };
    p.rows.push(`PERSISTED THREADS\nwerkstatt/baseline: ${result.nested} • foo: ${result.reverse} • missing base: ${result.missing}\nflat control after replay: ${result.control} • fresh blocked launch: ${result.fresh}`);
    p.show();
    return result;
  });
  await browser.evaluate(() => { const p = (window as any).branchProof; p.show("LAUNCH PREFLIGHT — REAL CLIENT RPC TRANSCRIPT", 0, 3); });
  await new Promise((resolve) => setTimeout(resolve, 1500));
  const preflight = await app.screenshot("branch-launch-results");
  await new Promise((resolve) => setTimeout(resolve, 500));
  const preflightStable = await app.screenshot("branch-launch-results-stable");
  if (!readFileSync(preflight).equals(readFileSync(preflightStable))) throw new Error("Preflight capture did not settle");
  await browser.evaluate(() => (window as any).branchProof.show("PROVISIONING AND ACCEPTED-COMMAND REPLAY — REAL RPC TRANSCRIPT", 3));
  await new Promise((resolve) => setTimeout(resolve, 500));
  const provisioning = await app.screenshot("provisioning-and-replay");
  await new Promise((resolve) => setTimeout(resolve, 500));
  const provisioningStable = await app.screenshot("provisioning-and-replay-stable");
  if (!readFileSync(provisioning).equals(readFileSync(provisioningStable))) throw new Error("Provisioning capture did not settle");
  expect(nested.accepted).toBe(false);
  expect(nested.message).toContain('branch "werkstatt" exists');
  expect(reverse.accepted).toBe(false);
  expect(reverse.message).toContain('branch "foo/bar" exists');
  expect(missing.accepted).toBe(false);
  expect(summary).toEqual({ nested: 0, reverse: 0, missing: 0, control: 1, fresh: 0 });
  expect(flat.accepted).toBe(true);
  expect(replay.accepted).toBe(true);
  expect(replay.resumed).toBe(true);
  expect(freshBlocked.accepted).toBe(false);
  for (const result of [provisionNested, provisionReverse]) {
    expect(result.detail).toBe("git branch failed");
    expect(result.reason).toBe("ref_name_conflict");
    expect(result.rawStderrExposed).toBe(false);
  }
});
