import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { createHash } from "node:crypto";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Readable, Writable } from "node:stream";
import { runCommand } from "./command";
import { repoRoot } from "./config";
import { runApplyPatchV2, type ApplyPatchV2Executor, type ApplyPatchV2RemoteResult } from "./apply-patch-v2";

interface GhContentRoute {
  repo: string;
  area: "root" | "pr-list" | "issue-list" | "pr-item" | "issue-item" | "legacy-item";
  number: number | null;
  floor: number | null;
  raw: string;
}

interface GhRouteDocument {
  kind: "pr" | "issue";
  repo: string;
  number: number;
  floor: number;
  title: string;
  url: string;
  state: string;
  commentCount: number;
  body: string;
  bodySha: string | null;
}

interface CommandResult {
  ok: boolean;
  exitCode: number | null;
  stdout: string;
  stderr: string;
  parsed: Record<string, unknown>;
}

type GhListState = "open" | "closed" | "all";

interface GhListOptions {
  limit: number;
  full: boolean;
  state: GhListState;
  search: string | null;
  labels: string[];
}

function record(value: unknown): Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : {};
}

function nested(value: unknown, keys: string[]): unknown {
  let current = value;
  for (const key of keys) current = record(current)[key];
  return current;
}

function cliJson(args: string[], timeoutMs = 80_000): CommandResult {
  const result = runCommand(["bun", "scripts/cli.ts", ...args], repoRoot, { timeoutMs });
  let parsed: Record<string, unknown> = {};
  if (result.stdout.trim().length > 0) {
    try {
      parsed = record(JSON.parse(result.stdout) as unknown);
    } catch {
      parsed = {};
    }
  }
  const ok = result.exitCode === 0 && parsed.ok !== false && nested(parsed, ["data", "ok"]) !== false;
  return { ok, exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr, parsed };
}

function decodeSegment(segment: string): string {
  try {
    return decodeURIComponent(segment);
  } catch {
    throw new Error(`invalid percent-encoded gh route segment: ${segment}`);
  }
}

export function isGhContentRoute(target: string | undefined): boolean {
  return typeof target === "string" && target.startsWith("gh:");
}

export function parseGhContentRoute(target: string): GhContentRoute {
  if (!target.startsWith("gh:/")) throw new Error("gh route must use gh:/owner/repo, for example: gh:/pikasTech/HWLAB/pr ls or gh:/pikasTech/HWLAB/pr/503/1 cat");
  const parts = target.slice("gh:/".length).split("/").filter((part) => part.length > 0).map(decodeSegment);
  if (parts.length < 2 || parts.length > 5) throw new Error("gh route supports gh:/owner/repo, gh:/owner/repo/pr, gh:/owner/repo/issue, gh:/owner/repo/pr/number[/floor], gh:/owner/repo/issue/number[/floor], and legacy gh:/owner/repo/number/floor");
  const [owner, repoName, areaRaw, numberRaw, floorRaw] = parts;
  if (!owner || !repoName) throw new Error("gh route requires non-empty owner and repo");
  const repo = `${owner}/${repoName}`;
  if (areaRaw === undefined) return { repo, area: "root", number: null, floor: null, raw: target };
  if (areaRaw === "pr" || areaRaw === "prs" || areaRaw === "pull" || areaRaw === "pulls") {
    if (numberRaw === undefined) return { repo, area: "pr-list", number: null, floor: null, raw: target };
    const number = positiveRouteNumber(numberRaw, "PR number");
    const floor = floorRaw === undefined ? null : positiveRouteNumber(floorRaw, "PR floor");
    return { repo, area: "pr-item", number, floor, raw: target };
  }
  if (areaRaw === "issue" || areaRaw === "issues") {
    if (numberRaw === undefined) return { repo, area: "issue-list", number: null, floor: null, raw: target };
    const number = positiveRouteNumber(numberRaw, "issue number");
    const floor = floorRaw === undefined ? null : positiveRouteNumber(floorRaw, "issue floor");
    return { repo, area: "issue-item", number, floor, raw: target };
  }
  if (parts.length === 4) {
    const number = positiveRouteNumber(areaRaw, "legacy issue/PR number");
    const floor = positiveRouteNumber(numberRaw ?? "", "legacy floor");
    return { repo, area: "legacy-item", number, floor, raw: target };
  }
  throw new Error("unsupported gh route; use gh:/owner/repo/pr[/number[/floor]] or gh:/owner/repo/issue[/number[/floor]]");
}

function positiveRouteNumber(raw: string, label: string): number {
  const value = Number(raw);
  if (!Number.isInteger(value) || value <= 0) throw new Error(`gh route ${label} must be a positive integer`);
  return value;
}

function commandData(result: CommandResult): Record<string, unknown> {
  return record(expandedParsedRoot(result).data);
}

function expandedParsedRoot(result: CommandResult): Record<string, unknown> {
  const dumpPath = nested(result.parsed, ["data", "dump", "path"]);
  if (typeof dumpPath === "string" && existsSync(dumpPath)) {
    try {
      return record(JSON.parse(readFileSync(dumpPath, "utf8")) as unknown);
    } catch {
      return result.parsed;
    }
  }
  return result.parsed;
}

function readDocument(route: GhContentRoute): GhRouteDocument {
  if (route.number === null) throw new Error("gh route document requires a PR or issue number");
  if (route.floor !== 1) {
    throw new Error("only floor 1 is currently supported; floor 1 maps to the issue/PR body");
  }
  const shouldReadPr = route.area === "pr-item" || route.area === "legacy-item";
  const shouldReadIssue = route.area === "issue-item" || route.area === "legacy-item";
  const pr = shouldReadPr ? cliJson(["gh", "pr", "read", String(route.number), "--repo", route.repo, "--json", "title,body,url,stateDetail,merged,mergedAt,mergeCommit,headRefName,baseRefName", "--raw"]) : null;
  if (pr?.ok) {
    const data = commandData(pr);
    const prRecord = record(data.pullRequest);
    const json = record(data.json);
    const issueMeta = readIssueMetadata(route.repo, route.number);
    return {
      kind: "pr",
      repo: route.repo,
      number: route.number,
      floor: route.floor,
      title: String(json.title ?? prRecord.title ?? ""),
      url: String(json.url ?? prRecord.url ?? ""),
      state: issueMeta.state ?? String(json.stateDetail ?? prRecord.stateDetail ?? ""),
      commentCount: issueMeta.commentCount ?? 0,
      body: String(json.body ?? prRecord.body ?? ""),
      bodySha: null,
    };
  }
  if (!shouldReadIssue) {
    throw new Error(`failed to read ${route.repo}#${route.number} as PR: ${pr?.stderr || pr?.stdout}`);
  }
  const issue = cliJson(["gh", "issue", "read", String(route.number), "--repo", route.repo, "--json", "title,body,state,updatedAt", "--raw"]);
  if (!issue.ok) {
    throw new Error(`failed to read ${route.repo}#${route.number} as ${route.area === "issue-item" ? "issue" : "PR or issue"}: pr=${pr?.stderr || pr?.stdout || "skipped"}; issue=${issue.stderr || issue.stdout}`);
  }
  const issueRecord = record(commandData(issue).issue);
  const json = record(commandData(issue).json);
  const commentCount = numberFromUnknown(issueRecord.commentCount) ?? commentsCount(commandData(issue).comments) ?? commentsCount(json.comments) ?? 0;
  return {
    kind: "issue",
    repo: route.repo,
    number: route.number,
    floor: route.floor,
    title: String(json.title ?? issueRecord.title ?? ""),
    url: String(issueRecord.url ?? ""),
    state: String(json.state ?? issueRecord.state ?? ""),
    commentCount,
    body: String(json.body ?? issueRecord.body ?? ""),
    bodySha: typeof issueRecord.bodySha === "string" ? issueRecord.bodySha : null,
  };
}

function documentRoute(route: GhContentRoute): GhContentRoute {
  if ((route.area === "pr-item" || route.area === "issue-item" || route.area === "legacy-item") && route.floor === null) {
    return { ...route, floor: 1 };
  }
  return route;
}

function isPatchOperation(operation: string): boolean {
  return operation === "patch-apply" || operation === "apply-patch" || operation === "apply_patch";
}

function numberFromUnknown(value: unknown): number | null {
  return typeof value === "number" && Number.isFinite(value) ? value : null;
}

function commentsCount(value: unknown): number | null {
  return Array.isArray(value) ? value.length : null;
}

function readIssueMetadata(repo: string, number: number): { state: string | null; commentCount: number | null; bodyChars: number | null } {
  const issue = cliJson(["gh", "issue", "read", String(number), "--repo", repo, "--json", "title,body,state,updatedAt,comments", "--raw"], 80_000);
  if (!issue.ok) return { state: null, commentCount: null, bodyChars: null };
  const data = commandData(issue);
  const issueRecord = record(data.issue);
  const json = record(data.json);
  const body = String(json.body ?? issueRecord.body ?? "");
  return {
    state: typeof json.state === "string" ? json.state : typeof issueRecord.state === "string" ? issueRecord.state : null,
    commentCount: numberFromUnknown(issueRecord.commentCount) ?? commentsCount(data.comments) ?? commentsCount(json.comments),
    bodyChars: body.length,
  };
}

function parseListState(raw: string): GhListState {
  if (raw === "open" || raw === "closed" || raw === "all") return raw;
  throw new Error("gh route list --state must be open, closed, or all");
}

function optionValue(args: string[], index: number, name: string): { value: string; nextIndex: number } {
  const arg = args[index] ?? "";
  if (arg.startsWith(`${name}=`)) return { value: arg.slice(name.length + 1), nextIndex: index };
  const value = args[index + 1];
  if (value === undefined || value.length === 0) throw new Error(`gh route list ${name} requires a value`);
  return { value, nextIndex: index + 1 };
}

function parseLabels(raw: string): string[] {
  const labels = raw.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
  if (labels.length === 0) throw new Error("gh route issue ls --label requires at least one non-empty label");
  return labels;
}

function parseListOptions(args: string[], kind: "pr" | "issue"): GhListOptions {
  const options: GhListOptions = { limit: 30, full: false, state: "all", search: null, labels: [] };
  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index] ?? "";
    if (arg === "--full") {
      options.full = true;
      continue;
    }
    if (arg === "--limit" || arg.startsWith("--limit=")) {
      const parsed = optionValue(args, index, "--limit");
      options.limit = positiveRouteNumber(parsed.value, "--limit");
      index = parsed.nextIndex;
      continue;
    }
    if (arg === "--state" || arg.startsWith("--state=")) {
      const parsed = optionValue(args, index, "--state");
      options.state = parseListState(parsed.value);
      index = parsed.nextIndex;
      continue;
    }
    if (arg === "--search" || arg.startsWith("--search=")) {
      if (kind !== "issue") throw new Error("gh route --search is only supported for issue ls");
      const parsed = optionValue(args, index, "--search");
      options.search = parsed.value;
      index = parsed.nextIndex;
      continue;
    }
    if (arg === "--label" || arg.startsWith("--label=")) {
      if (kind !== "issue") throw new Error("gh route --label is only supported for issue ls");
      const parsed = optionValue(args, index, "--label");
      options.labels.push(...parseLabels(parsed.value));
      index = parsed.nextIndex;
      continue;
    }
    throw new Error(`unsupported gh route ${kind} ls arg: ${arg}`);
  }
  return options;
}

function listRoute(route: GhContentRoute, args: string[]): number {
  if (route.area === "root") {
    printDirectory([
      { name: "pr/", type: "dir", description: "pull request directory" },
      { name: "issue/", type: "dir", description: "issue directory" },
    ]);
    return 0;
  }
  if (route.area === "pr-list") {
    const options = parseListOptions(args, "pr");
    const result = cliJson(["gh", "pr", "list", "--repo", route.repo, "--state", options.state, "--limit", String(options.limit), "--json", "number,title,state,url,updatedAt,createdAt,author,headRefName,baseRefName,draft"], 80_000);
    if (!result.ok) throw new Error(result.stderr || result.stdout || "gh pr list failed");
    const prs = commandData(result).pullRequests;
    const rows = Array.isArray(prs) ? prs.map((item) => prDirectoryEntry(route.repo, record(item), options.full)) : [];
    printDirectory(rows);
    return 0;
  }
  if (route.area === "issue-list") {
    const options = parseListOptions(args, "issue");
    const issueArgs = ["gh", "issue", "list", "--repo", route.repo, "--state", options.state, "--limit", String(options.limit), "--json", "number,title,state,url,updatedAt,createdAt,author"];
    if (options.search !== null) issueArgs.push("--search", options.search);
    for (const label of options.labels) issueArgs.push("--label", label);
    const result = cliJson(issueArgs, 80_000);
    if (!result.ok) throw new Error(result.stderr || result.stdout || "gh issue list failed");
    const issues = commandData(result).issues;
    const rows = Array.isArray(issues) ? issues.map((item) => issueDirectoryEntry(route.repo, record(item), options.full)) : [];
    printDirectory(rows);
    return 0;
  }
  const document = readDocument({ ...route, floor: 1 });
  printDirectory([
    {
      name: "1",
      type: "file",
      kind: document.kind,
      state: document.state,
      floors: 1 + document.commentCount,
      commentCount: document.commentCount,
      bodyChars: document.body.length,
      title: document.title,
      url: document.url,
    },
  ]);
  return 0;
}

function prDirectoryEntry(repo: string, item: Record<string, unknown>, full: boolean): Record<string, unknown> {
  const number = Number(item.number);
  const meta = full && Number.isInteger(number) ? readIssueMetadata(repo, number) : null;
  const commentCount = meta?.commentCount ?? null;
  return {
    name: `${item.number}/`,
    type: "dir",
    kind: "pr",
    state: meta?.state ?? item.state ?? null,
    draft: item.draft === true,
    floors: commentCount === null ? 1 : 1 + commentCount,
    commentCount,
    bodyChars: meta?.bodyChars ?? null,
    title: item.title ?? "",
    url: item.url ?? `https://github.com/${repo}/pull/${item.number}`,
    headRefName: item.headRefName ?? null,
    baseRefName: item.baseRefName ?? null,
    updatedAt: item.updatedAt ?? null,
  };
}

function issueDirectoryEntry(repo: string, item: Record<string, unknown>, full: boolean): Record<string, unknown> {
  const number = Number(item.number);
  const meta = full && Number.isInteger(number) ? readIssueMetadata(repo, number) : null;
  const commentCount = meta?.commentCount ?? null;
  return {
    name: `${item.number}/`,
    type: "dir",
    kind: "issue",
    state: meta?.state ?? item.state ?? null,
    floors: commentCount === null ? 1 : 1 + commentCount,
    commentCount,
    bodyChars: meta?.bodyChars ?? null,
    title: item.title ?? "",
    url: item.url ?? `https://github.com/${repo}/issues/${item.number}`,
    updatedAt: item.updatedAt ?? null,
  };
}

function printDirectory(rows: Array<Record<string, unknown>>): void {
  for (const row of rows) {
    const fields = [
      String(row.name ?? ""),
      `type=${String(row.type ?? "")}`,
      row.kind === undefined ? null : `kind=${String(row.kind)}`,
      row.state === undefined || row.state === null ? null : `state=${String(row.state)}`,
      row.draft === undefined ? null : `draft=${String(row.draft)}`,
      row.floors === undefined || row.floors === null ? null : `floors=${String(row.floors)}`,
      row.commentCount === undefined || row.commentCount === null ? null : `commentCount=${String(row.commentCount)}`,
      row.bodyChars === undefined || row.bodyChars === null ? null : `bodyChars=${String(row.bodyChars)}`,
      row.updatedAt === undefined || row.updatedAt === null ? null : `updatedAt=${String(row.updatedAt)}`,
      row.url === undefined || row.url === null || row.url === "" ? null : `url=${String(row.url)}`,
      row.title === undefined || row.title === "" ? null : `title=${String(row.title)}`,
    ].filter((item): item is string => item !== null);
    process.stdout.write(`${fields.join("\t")}\n`);
  }
}

function readStdin(): Promise<string> {
  return new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    process.stdin.on("data", (chunk: Buffer) => chunks.push(chunk));
    process.stdin.on("error", reject);
    process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
  });
}

function sha256Hex(value: Buffer): string {
  return createHash("sha256").update(value).digest("hex");
}

function bodyVirtualPath(path: string): string | null {
  const normalized = path.replace(/\\/gu, "/").replace(/^\.\/+/u, "");
  if (normalized === "body.md" || normalized === "body" || normalized === "1.md") return "body.md";
  return null;
}

function verifyVirtualWrite(target: string, buffer: Buffer, expectedBytes: string, expectedSha256: string): ApplyPatchV2RemoteResult | null {
  const expectedByteCount = Number(expectedBytes);
  if (!Number.isSafeInteger(expectedByteCount) || expectedByteCount < 0) {
    return { exitCode: 2, stdout: "", stderr: `invalid expected byte count for ${target}\n` };
  }
  if (buffer.length !== expectedByteCount) {
    return { exitCode: 23, stdout: "", stderr: `v2 virtual byte count mismatch for ${target}: expected=${expectedByteCount} actual=${buffer.length}\n` };
  }
  const actualSha256 = sha256Hex(buffer);
  if (actualSha256 !== expectedSha256) {
    return { exitCode: 24, stdout: "", stderr: `v2 virtual sha256 mismatch for ${target}: expected=${expectedSha256} actual=${actualSha256}\n` };
  }
  return null;
}

export class GhVirtualFileExecutor implements ApplyPatchV2Executor {
  private readonly files = new Map<string, Buffer>();
  private readonly temp = new Map<string, string>();

  constructor(body: string) {
    this.files.set("body.md", Buffer.from(body, "utf8"));
  }

  body(): string {
    const body = this.files.get("body.md");
    return body === undefined ? "" : body.toString("utf8");
  }

  async run(command: string[], input?: string): Promise<ApplyPatchV2RemoteResult> {
    const operation = command[4] ?? "";
    const args = command.slice(5);
    try {
      return this.runOperation(operation, args, input);
    } catch (error) {
      return { exitCode: 1, stdout: "", stderr: `${error instanceof Error ? error.message : String(error)}\n` };
    }
  }

  private canonical(rawPath: string): string {
    const path = bodyVirtualPath(rawPath);
    if (path === null) throw new Error(`unsupported gh virtual file path: ${rawPath}; use body.md for floor 1`);
    return path;
  }

  private file(path: string): Buffer {
    const body = this.files.get(path);
    if (body === undefined) throw new Error(`virtual file not found: ${path}`);
    return body;
  }

  private runOperation(operation: string, args: string[], input?: string): ApplyPatchV2RemoteResult {
    if (operation === "stat") {
      const path = this.canonical(args[0] ?? "");
      const body = this.file(path);
      return { exitCode: 0, stdout: `${body.length} ${sha256Hex(body)}\n`, stderr: "" };
    }
    if (operation === "read-b64-block") {
      const path = this.canonical(args[0] ?? "");
      const blockIndex = Number(args[1] ?? "");
      const blockSize = Number(args[2] ?? "");
      if (!Number.isSafeInteger(blockIndex) || blockIndex < 0 || !Number.isSafeInteger(blockSize) || blockSize <= 0) {
        return { exitCode: 2, stdout: "", stderr: "invalid read block args\n" };
      }
      const body = this.file(path);
      const offset = blockIndex * blockSize;
      return { exitCode: 0, stdout: body.subarray(offset, offset + blockSize).toString("base64"), stderr: "" };
    }
    if (operation === "write-b64-argv") {
      const path = this.canonical(args[0] ?? "");
      const expectedBytes = args[1] ?? "";
      const expectedSha256 = args[2] ?? "";
      const buffer = Buffer.from(args.slice(3).join(""), "base64");
      const failure = verifyVirtualWrite(path, buffer, expectedBytes, expectedSha256);
      if (failure !== null) return failure;
      this.files.set(path, buffer);
      return { exitCode: 0, stdout: "", stderr: "" };
    }
    if (operation === "write-b64-stdin") {
      const path = this.canonical(args[0] ?? "");
      const expectedBytes = args[1] ?? "";
      const expectedSha256 = args[2] ?? "";
      const buffer = Buffer.from((input ?? "").replace(/\s+/gu, ""), "base64");
      const failure = verifyVirtualWrite(path, buffer, expectedBytes, expectedSha256);
      if (failure !== null) return failure;
      this.files.set(path, buffer);
      return { exitCode: 0, stdout: "", stderr: "" };
    }
    if (operation === "write-b64-begin") {
      this.canonical(args[0] ?? "");
      const token = args[1] ?? "";
      this.temp.set(token, "");
      return { exitCode: 0, stdout: "", stderr: "" };
    }
    if (operation === "write-b64-append") {
      this.canonical(args[0] ?? "");
      const token = args[1] ?? "";
      if (!this.temp.has(token)) return { exitCode: 2, stdout: "", stderr: "unknown virtual write token\n" };
      this.temp.set(token, `${this.temp.get(token) ?? ""}${args[2] ?? ""}`);
      return { exitCode: 0, stdout: "", stderr: "" };
    }
    if (operation === "write-b64-commit") {
      const path = this.canonical(args[0] ?? "");
      const token = args[1] ?? "";
      const expectedBytes = args[2] ?? "";
      const expectedSha256 = args[3] ?? "";
      const encoded = this.temp.get(token);
      if (encoded === undefined) return { exitCode: 2, stdout: "", stderr: "unknown virtual write token\n" };
      const buffer = Buffer.from(encoded, "base64");
      const failure = verifyVirtualWrite(path, buffer, expectedBytes, expectedSha256);
      if (failure !== null) return failure;
      this.temp.delete(token);
      this.files.set(path, buffer);
      return { exitCode: 0, stdout: "", stderr: "" };
    }
    if (operation === "delete") {
      const path = this.canonical(args[0] ?? "");
      if (!this.files.has(path)) return { exitCode: 1, stdout: "", stderr: `virtual file not found: ${path}\n` };
      this.files.delete(path);
      return { exitCode: 0, stdout: "", stderr: "" };
    }
    if (operation === "move") {
      const from = this.canonical(args[0] ?? "");
      const to = this.canonical(args[1] ?? "");
      const body = this.file(from);
      this.files.set(to, body);
      this.files.delete(from);
      return { exitCode: 0, stdout: "", stderr: "" };
    }
    return { exitCode: 2, stdout: "", stderr: `unsupported virtual apply-patch operation: ${operation}\n` };
  }
}

function writableCapture(): { stream: Writable; text: () => string } {
  const chunks: Buffer[] = [];
  return {
    stream: new Writable({
      write(chunk, _encoding, callback) {
        chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
        callback();
      },
    }),
    text: () => Buffer.concat(chunks).toString("utf8"),
  };
}

export async function applyPatchToGhBody(currentBody: string, patchText: string): Promise<{ body: string; applyPatchOutput: string; executor: GhVirtualFileExecutor }> {
  const executor = new GhVirtualFileExecutor(currentBody);
  const output = writableCapture();
  const exitCode = await runApplyPatchV2({
    executor,
    stdin: Readable.from([patchText]),
    stdout: output.stream,
    argv: [],
  });
  if (exitCode !== 0) throw new Error(`apply-patch-v2 failed for gh virtual body: ${output.text()}`);
  return { body: executor.body(), applyPatchOutput: output.text(), executor };
}

function writeTempBody(body: string): string {
  const dir = mkdtempSync(join(tmpdir(), "unidesk-gh-route-"));
  const path = join(dir, "body.md");
  writeFileSync(path, body, "utf8");
  return path;
}

function updateDocument(document: GhRouteDocument, nextBody: string, dryRun: boolean): CommandResult {
  const bodyPath = writeTempBody(nextBody);
  try {
    if (document.kind === "pr") {
      return cliJson(["gh", "pr", "update", String(document.number), "--repo", document.repo, "--mode", "replace", "--body-file", bodyPath, ...(dryRun ? ["--dry-run"] : [])], 100_000);
    }
    return cliJson([
      "gh",
      "issue",
      "update",
      String(document.number),
      "--repo",
      document.repo,
      "--mode",
      "replace",
      "--body-file",
      bodyPath,
      ...(document.bodySha !== null ? ["--expect-body-sha", document.bodySha] : []),
      ...(dryRun ? ["--dry-run"] : []),
    ], 100_000);
  } finally {
    rmSync(join(bodyPath, ".."), { recursive: true, force: true });
  }
}

function printJson(payload: unknown): void {
  process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
}

function runRg(body: string, args: string[]): number {
  if (args.length === 0) throw new Error("gh route rg requires a pattern");
  const dir = mkdtempSync(join(tmpdir(), "unidesk-gh-route-rg-"));
  try {
    const bodyPath = join(dir, "body.md");
    writeFileSync(bodyPath, body, "utf8");
    const result = runCommand(["rg", "--color=never", ...args, bodyPath], repoRoot, { timeoutMs: 30_000 });
    process.stdout.write(result.stdout.replaceAll(`${bodyPath}:`, ""));
    process.stderr.write(result.stderr.replaceAll(`${bodyPath}:`, ""));
    return result.exitCode ?? 1;
  } finally {
    rmSync(dir, { recursive: true, force: true });
  }
}

export async function runGhContentRoute(target: string, args: string[]): Promise<number> {
  const route = parseGhContentRoute(target);
  const operation = args[0] ?? "cat";
  const opArgs = args.slice(1);
  if (operation === "ls" || operation === "dir") {
    return listRoute(route, opArgs);
  }
  const document = readDocument(documentRoute(route));
  if (operation === "cat") {
    process.stdout.write(document.body);
    return 0;
  }
  if (operation === "rg") {
    return runRg(document.body, opArgs);
  }
  if (isPatchOperation(operation)) {
    const dryRun = opArgs.includes("--dry-run");
    const unsupported = opArgs.filter((arg) => arg !== "--dry-run");
    if (unsupported.length > 0) throw new Error(`unsupported gh ${operation} args: ${unsupported.join(" ")}`);
    const patchText = await readStdin();
    const patchResult = await applyPatchToGhBody(document.body, patchText);
    const nextBody = patchResult.body;
    const update = updateDocument(document, nextBody, dryRun);
    printJson({
      ok: update.ok,
      route,
      target: {
        kind: document.kind,
        repo: document.repo,
        number: document.number,
        floor: document.floor,
        title: document.title,
        url: document.url,
      },
      dryRun,
      bodyCharsBefore: document.body.length,
      bodyCharsAfter: nextBody.length,
      applyPatch: {
        engine: "apply-patch-v2",
        virtualFiles: ["body.md"],
        output: patchResult.applyPatchOutput.trim(),
      },
      update: commandData(update),
    });
    return update.ok ? 0 : 1;
  }
  if (operation === "rm") {
    printJson({
      ok: false,
      degradedReason: "unsupported-operation",
      route,
      message: "gh route floor 1 is the issue/PR body and cannot be removed safely; use apply-patch to delete reviewed sections.",
    });
    return 2;
  }
  printJson({
    ok: false,
    degradedReason: "unsupported-command",
    route,
    supported: ["cat", "rg", "patch-apply", "apply-patch", "apply_patch", "rm"],
  });
  return 2;
}
