import { expect } from "e2e";
import { execFileSync } from "node:child_process";
import { mkdirSync, writeFileSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { makeState, runPromise } from "./t3.ts";
import { test, IOS } from "./t3-mobile.ts";
import { APP_DIR, MOBILE_T3_HOME, MOBILE } from "../e2e.config.ts";

// Public, synthetic fixture paths: no developer checkout paths appear in the UI.
const root = "/tmp/file-chip-demo-35337";
const git = (cwd: string, ...args: string[]) => execFileSync("git", args, { cwd });

test("Reply file chips open the unregistered checkout the agent worked in on iOS", IOS, async ({ t3, app, screen, device }) => {
  mkdirSync(root, { recursive: true });
  const a = join(root, "repo-a");
  const b = join(root, "repo-b");
  const work = join(root, "repo-b-feature");
  for (const dir of [a, b]) {
    mkdirSync(dir, { recursive: true });
    git(dir, "init", "-q", "--initial-branch=main");
    writeFileSync(join(dir, "README.md"), "Synthetic file link demonstration\n");
    git(dir, "add", ".");
    git(dir, "-c", "user.name=Scenario", "-c", "user.email=scenario@example.invalid", "commit", "-qm", "Fixture", "--allow-empty");
  }
  try { git(b, "worktree", "add", "-b", "feature", work); } catch { /* reused fixture */ }
  mkdirSync(join(work, "docs/guides"), { recursive: true });
  mkdirSync(join(work, "server"), { recursive: true });
  writeFileSync(join(work, "docs/guides/new_guide.md"), "# New guide\n\nGUIDE_FROM_REPO_B_FEATURE\n");
  writeFileSync(join(work, "server/config.yaml"), "source: REPO_B_FEATURE\nconfig_marker: CONFIG_FROM_REPO_B_FEATURE\n");
  // The bare name lives below the checkout root, exercising directory search rather than literal joining.
  writeFileSync(join(work, "server/config.test.yaml"), "test_marker: BARE_NAME_FROM_REPO_B_FEATURE\nenabled: true\n");
  const transcript = join(APP_DIR, "apps/server/scripts/dev-replay/showcase_transcript.ndjson");
  const original = readFileSync(transcript, "utf8");
  const update = (u: unknown) => ({ type: "emit_inbound", frame: { kind: "notification", method: "session/update", params: { sessionId: "repro", update: u } } });
  const command = `cd ${work} && git status`;
  const reply = "`docs/guides/new_guide.md` is the new guide. `server/config.yaml` and [config.test.yaml](config.test.yaml) each gain two lines.";
  writeFileSync(transcript, [
    { type: "expect_outbound", frame: { kind: "request", method: "session/prompt" } },
    update({ sessionUpdate: "tool_call", toolCallId: "status", kind: "execute", title: command, rawInput: { command }, status: "pending" }),
    update({ sessionUpdate: "tool_call_update", toolCallId: "status", status: "completed", content: [{ type: "content", content: { type: "text", text: "On branch feature\nWorking tree contains new guide and configuration files." } }] }),
    update({ sessionUpdate: "agent_message_chunk", content: { type: "text", text: reply } }),
    { type: "emit_inbound", frame: { kind: "response", method: "session/prompt", result: { stopReason: "end_turn" } } },
  ].map(x => JSON.stringify(x)).join("\n"));
  try {
    const { cli } = makeState(MOBILE_T3_HOME, { T3CODE_PORT: String(MOBILE.serverPort) });
    await runPromise(cli(["project", "add", a, "--title", "File chip demo"]));
    const thread = await t3.thread({ project: a, title: "Files from another checkout", messages: ["Report the guide and configuration changes."], model: "showcase" });
    await t3.open();
    await screen.getByText("Files from another checkout", { exact: false }).first().tap();
    await app.screenshot("mobile-reply-file-chips");
    for (const [name, marker, label] of [
      ["new_guide.md", "GUIDE_FROM_REPO_B_FEATURE", "opened-guide"],
      ["config.yaml", "CONFIG_FROM_REPO_B_FEATURE", "opened-config"],
      ["config.test.yaml", "BARE_NAME_FROM_REPO_B_FEATURE", "opened-bare-name"],
    ]) {
      await screen.getByRole("link", name, { exact: false }).first().tap();
      await screen.getByRole("button", "File actions").tap();
      const copy = screen.getByText("Copy contents", { exact: true });
      const canCopy = (await copy.count()) > 0;
      await (canCopy ? copy : screen.getByText("Copy path", { exact: true })).tap();
      const contents = canCopy ? await device.clipboard() : "";
      await expect.soft(contents).toContain(marker);
      let previous = await app.screenshot(`${label}-settling`);
      for (let attempt = 0; attempt < 8; attempt++) {
        await new Promise(resolve => setTimeout(resolve, 500));
        const current = await app.screenshot(`${label}-stable-${attempt}`);
        if (readFileSync(previous).equals(readFileSync(current))) break;
        previous = current;
        if (attempt === 7) throw new Error("Capture did not stabilize");
      }
      await app.screenshot(label);
      await app.back();
    }
  } finally { writeFileSync(transcript, original); }
});
