/**
 * Tests for memory-doctor.ts diagnostic gaps.
 *
 * memory-doctor runs ripgrep --version and reports status. This file covers
 * the four gaps fixed in PR for issue #123:
 *   G9 — allGreen must check rg --version status===0, not just rgBin!==null
 *   G2  — arch-mismatch detection when binary platform != runtime platform
 *   G3  — npm require-error surfaced in report when npm package missing
 *
 * Runtime note: opencode ships a standalone binary with Bun bundled inside;
 * there is no separate `bun` CLI on the host (CI runner uses node + pytest).
 * This file documents the intended TS-side test cases and is runnable under
 * `bun test` once a bun runtime is available on the host.
 *
 * Test cases:
 *   - test_allGreen_false_on_broken_binary — rgPath exists but --version
 *     fails → report says NOT all green (G9)
 *   - test_arch_mismatch_detection — rgBin path contains 'darwin' but
 *     process.platform === 'linux' → report mentions arch mismatch (G2)
 *   - test_npm_missing_hint_in_report — rgBin===null, no system rg → report
 *     includes npm ci hint (G3)
 *   - test_allGreen_true_when_rg_works — rg --version succeeds → keyword
 *     works (sanity, G9 regression guard)
 */

import { describe, test, expect, mock } from "bun:test" with { type: "'bun-test'" }
import fs from "fs"
import os from "os"
import path from "path"

const DOCTOR_SRC = path.resolve(
  import.meta.dir,
  "..",
  ".opencode",
  "tools",
  "memory-doctor.ts"
)

function ctx(worktree = ".") {
  return {
    sessionID: "t",
    messageID: "t",
    agent: "t",
    directory: ".",
    worktree,
    abort: new AbortController().signal,
    metadata() {},
    async ask() {},
  }
}

describe("memory-doctor allGreen (G9)", () => {
  test("test_allGreen_false_on_broken_binary — rgPath exists but --version fails → NOT all green", async () => {
    const fakeRgPath = "/tmp/opencode/broken-rg"
    mock.module("node:module", () => ({
      createRequire: () => () => ({ rgPath: fakeRgPath }),
    }))
    mock.module("fs", () => ({
      ...fs,
      existsSync: (p: string) =>
        p === fakeRgPath || p === os.homedir() + "/.local/share/opencode/opencode-memory",
      readdirSync: () => [],
      statSync: () => ({ size: 0, mtime: new Date() }),
    }))
    mock.module("child_process", () => ({
      spawnSync: (cmd: string, args: string[]) => {
        if (cmd === fakeRgPath && args[0] === "--version") {
          return { status: 1, stdout: "", stderr: "cannot execute binary" }
        }
        if (cmd === "python3") return { status: 0, stdout: "ok\n", stderr: "" }
        if (cmd === "rg") return { status: 1, stdout: "", stderr: "" }
        return { status: 0, stdout: "", stderr: "" }
      },
    }))
    const mod = await import(DOCTOR_SRC + "?t=" + Date.now())
    const report = await mod.default.execute({}, ctx())
    expect(report).toContain("ripgrep missing or broken")
  })

  test("test_allGreen_true_when_rg_works — rg --version succeeds → keyword works", async () => {
    const fakeRgPath = "/tmp/opencode/good-rg"
    mock.module("node:module", () => ({
      createRequire: () => () => ({ rgPath: fakeRgPath }),
    }))
    mock.module("fs", () => ({
      ...fs,
      existsSync: () => true,
      readdirSync: () => [],
      statSync: () => ({ size: 0, mtime: new Date() }),
    }))
    mock.module("child_process", () => ({
      spawnSync: (cmd: string, args: string[]) => {
        if (cmd === fakeRgPath && args[0] === "--version") {
          return { status: 0, stdout: "ripgrep 13.0.0\n", stderr: "" }
        }
        if (cmd === "python3") return { status: 0, stdout: "ok\n", stderr: "" }
        return { status: 0, stdout: "", stderr: "" }
      },
    }))
    const mod = await import(DOCTOR_SRC + "?t=" + Date.now())
    const report = await mod.default.execute({}, ctx())
    expect(report).toContain("All green")
  })
})

describe("memory-doctor arch mismatch (G2)", () => {
  test("test_arch_mismatch_detection — darwin binary on linux → report mentions arch mismatch", async () => {
    // @vscode/ripgrep platform dir names include the OS: e.g.
    // .../@vscode/ripgrep/bin/rg-darwin-arm64 or .../rg-linux-x64.
    const fakeRgPath = "/tmp/opencode/@vscode/ripgrep/bin/rg-darwin-arm64"
    const origPlatform = process.platform
    Object.defineProperty(process, "platform", { value: "linux" })
    try {
      mock.module("node:module", () => ({
        createRequire: () => () => ({ rgPath: fakeRgPath }),
      }))
      mock.module("fs", () => ({
        ...fs,
        existsSync: () => true,
        readdirSync: () => [],
        statSync: () => ({ size: 0, mtime: new Date() }),
      }))
      mock.module("child_process", () => ({
        spawnSync: (cmd: string, args: string[]) => {
          if (cmd === fakeRgPath && args[0] === "--version") {
            return { status: 1, stdout: "", stderr: "Exec format error" }
          }
          if (cmd === "python3") return { status: 0, stdout: "ok\n", stderr: "" }
          return { status: 0, stdout: "", stderr: "" }
        },
      }))
      const mod = await import(DOCTOR_SRC + "?t=" + Date.now())
      const report = await mod.default.execute({}, ctx())
      expect(report).toContain("arch mismatch")
      expect(report).toContain("darwin")
    } finally {
      Object.defineProperty(process, "platform", { value: origPlatform })
    }
  })
})

describe("memory-doctor npm missing hint (G3)", () => {
  test("test_npm_missing_hint_in_report — rgBin===null, no system rg → report includes npm ci hint", async () => {
    mock.module("node:module", () => ({
      createRequire: () => () => {
        throw new Error("Cannot find module '@vscode/ripgrep'")
      },
    }))
    mock.module("fs", () => ({
      ...fs,
      existsSync: (p: string) =>
        p === os.homedir() + "/.local/share/opencode/opencode-memory",
      readdirSync: () => [],
      statSync: () => ({ size: 0, mtime: new Date() }),
    }))
    mock.module("child_process", () => ({
      spawnSync: (cmd: string) => {
        if (cmd === "rg") return { status: 127, stdout: "", stderr: "rg: not found" }
        if (cmd === "python3") return { status: 0, stdout: "ok\n", stderr: "" }
        return { status: 0, stdout: "", stderr: "" }
      },
    }))
    const mod = await import(DOCTOR_SRC + "?t=" + Date.now())
    const report = await mod.default.execute({}, ctx())
    expect(report).toContain("npm package missing")
    expect(report).toContain("npm ci")
    expect(report).toContain("ripgrep missing or broken")
  })
})