import { afterEach, describe, expect, test } from "bun:test";
import { prFiles, prView } from "./gh/auth-pr-read";
import { issueBodyReadCommands, prBodyReadCommands } from "./gh/client";
import { withGhDefaultRendered } from "./gh/default-render";
import { ghScopedHelp, ghScopedHelpNotes } from "./gh/help";
import { runGhCommand } from "./gh/index";
import { renderIssueCreateTable } from "./gh/issue-write";
import { readViewSupportedCommands } from "./gh/refs";

const originalFetch = globalThis.fetch;

afterEach(() => {
  globalThis.fetch = originalFetch;
});

function renderedText(value: unknown): string {
  return String((value as { renderedText?: unknown }).renderedText ?? "");
}

function nextSection(text: string): string {
  return text.split("Next:\n", 2)[1]?.split("\n\nDisclosure:", 1)[0] ?? "";
}

describe("gh default body read path", () => {
  test("puts trans gh issue cat first and keeps comments bounded", () => {
    const repo = "pikasTech/unidesk";
    const rendered = renderedText(withGhDefaultRendered(["issue", "view", "1890", "--repo", repo], {
      ok: true,
      command: "issue view",
      repo,
      issue: {
        number: 1890,
        title: "trans 文本短路径",
        state: "open",
        updatedAt: "2026-07-13T00:00:00Z",
        commentCount: 8,
        bodyChars: 30_000,
        bodySha: "abc123",
        bodyPreview: "preview",
        bodyOmitted: true,
      },
      readCommands: issueBodyReadCommands(repo, 1890),
    }));
    const next = nextSection(rendered);

    expect(next.trimStart().split("\n")[0]).toBe("trans gh:/pikasTech/unidesk/issue/1890 cat");
    expect(next).toContain("trans gh:/pikasTech/unidesk/issue/1890 rg <pattern>");
    expect(next).toContain("bun scripts/cli.ts gh issue comments 1890 --repo pikasTech/unidesk");
    expect(next).not.toContain("--json body");
    expect(next).not.toContain("--full");
    expect(rendered).not.toContain("/tmp/unidesk-cli-output");
  });

  test("puts trans gh PR cat before file review commands", () => {
    const repo = "pikasTech/unidesk";
    const rendered = renderedText(withGhDefaultRendered(["pr", "view", "1893", "--repo", repo], {
      ok: true,
      command: "pr view",
      repo,
      pullRequest: {
        number: 1893,
        title: "fix trans",
        state: "open",
        stateDetail: "open",
        head: { ref: "fix/trans" },
        base: { ref: "master" },
        bodyChars: 1_000,
        bodySha: "def456",
        bodyPreview: "preview",
        bodyOmitted: true,
      },
      readCommands: prBodyReadCommands(repo, 1893),
    }));
    const next = nextSection(rendered);

    expect(next.trimStart().split("\n")[0]).toBe("trans gh:/pikasTech/unidesk/pr/1893 cat");
    expect(next).toContain("trans gh:/pikasTech/unidesk/pr/1893 rg <pattern>");
    expect(next).toContain("bun scripts/cli.ts gh pr files 1893 --repo pikasTech/unidesk");
    expect(next).not.toContain("--json body");
    expect(next).not.toContain("--full");
    expect(rendered).not.toContain("/tmp/unidesk-cli-output");
  });

  test("keeps machine body disclosure explicit in shared read commands", () => {
    expect(issueBodyReadCommands("owner/repo", 12)).toMatchObject({
      body: "trans gh:/owner/repo/issue/12 cat",
      json: "bun scripts/cli.ts gh issue view 12 --repo owner/repo --json body",
    });
    expect(prBodyReadCommands("owner/repo", 34)).toMatchObject({
      body: "trans gh:/owner/repo/pr/34 cat",
      json: "bun scripts/cli.ts gh pr view 34 --repo owner/repo --json body",
    });
  });

  test("scoped help separates human body reads from machine disclosure", () => {
    const issueNotes = ghScopedHelpNotes(["issue", "view"]).join("\n");
    const prNotes = ghScopedHelpNotes(["pr", "view"]).join("\n");

    expect(issueNotes).toContain("trans gh:/owner/repo/issue/<number> cat");
    expect(issueNotes).toContain("explicit structured machine disclosure");
    expect(prNotes).toContain("trans gh:/owner/repo/pr/<number> cat");
    expect(prNotes).toContain("explicit structured machine disclosure");

    const issueHelp = JSON.stringify(ghScopedHelp(["issue", "view", "--help"]));
    const prHelp = JSON.stringify(ghScopedHelp(["pr", "view", "--help"]));
    expect(issueHelp).toContain("trans gh:/owner/repo/issue/<number> cat");
    expect(prHelp).toContain("trans gh:/owner/repo/pr/<number> cat");
  });

  test("uses PR cat after a complete bounded file listing", async () => {
    globalThis.fetch = (async (input: string | URL | Request) => {
      const url = new URL(typeof input === "string" || input instanceof URL ? input.toString() : input.url);
      if (url.pathname.endsWith("/pulls/42")) {
        return Response.json({
          id: 42,
          number: 42,
          title: "bounded PR",
          body: "body",
          state: "open",
          html_url: "https://github.com/owner/repo/pull/42",
          head: { ref: "fix/test", sha: "a".repeat(40) },
          base: { ref: "master", sha: "b".repeat(40) },
          changed_files: 1,
          additions: 2,
          deletions: 1,
          commits: 1,
        });
      }
      return Response.json([{ filename: "src/test.ts", status: "modified", additions: 2, deletions: 1, changes: 3 }]);
    }) as typeof fetch;

    const result = await prFiles("owner/repo", "token", 42, 30);

    expect((result.next as { command?: string }).command).toBe("trans gh:/owner/repo/pr/42 cat");
    expect(result.readCommands).toMatchObject({
      body: "trans gh:/owner/repo/pr/42 cat",
      metadata: "bun scripts/cli.ts gh pr view 42 --repo owner/repo",
    });

    const view = await prView("owner/repo", "token", 42, undefined);
    expect(view.readCommands).toMatchObject({
      body: "trans gh:/owner/repo/pr/42 cat",
      search: "trans gh:/owner/repo/pr/42 rg <pattern>",
    });
  });

  test("uses cat and rg after issue create and PR writes", () => {
    const issueRendered = renderIssueCreateTable({
      ok: true,
      command: "issue create",
      repo: "owner/repo",
      issue: { number: 12, title: "new issue", state: "open" },
    });
    const prRendered = renderedText(withGhDefaultRendered(["pr", "update", "34", "--repo", "owner/repo"], {
      ok: true,
      command: "pr update",
      repo: "owner/repo",
      number: 34,
      pullRequest: { number: 34, title: "updated PR", state: "open" },
    }));

    for (const [text, route] of [
      [issueRendered, "trans gh:/owner/repo/issue/12"],
      [prRendered, "trans gh:/owner/repo/pr/34"],
    ] as const) {
      const next = nextSection(text);
      expect(next).toContain(`${route} cat`);
      expect(next).toContain(`${route} rg <pattern>`);
      expect(next).not.toContain("--json body");
      expect(next).not.toContain("--full");
      expect(text).not.toContain("/tmp/unidesk-cli-output");
    }
  });

  test("keeps invalid --number guidance on human issue and PR routes", async () => {
    const result = await runGhCommand(["issue", "list", "--number", "12", "--repo", "owner/repo"]) as {
      supportedCommands?: string[];
    };

    expect(result.supportedCommands).toEqual([
      "trans gh:/owner/name/issue/<number> cat",
      "trans gh:/owner/name/issue/<number> rg <pattern>",
      "trans gh:/owner/name/pr/<number> cat",
      "trans gh:/owner/name/pr/<number> rg <pattern>",
    ]);
    expect(readViewSupportedCommands("issue", "owner/repo", 12)).toEqual([
      "trans gh:/owner/repo/issue/12 cat",
      "trans gh:/owner/repo/issue/12 rg <pattern>",
      "bun scripts/cli.ts gh issue view 12 --repo owner/repo",
    ]);
  });
});
