import test from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";

import {
  buildCodeIntelligenceContextBlock,
  detectProjectRag,
  ensureProjectRagMcpConfig,
  findProjectRagSourceDir,
  formatProjectRagStatus,
  PROJECT_RAG_MCP_SERVER_NAME,
  resolveProjectRagBuildJobs,
  resolveProjectRagBinaryForProject,
} from "../code-intelligence.ts";

function makeProject(): string {
  const projectRoot = mkdtempSync(join(tmpdir(), "sf-code-intel-"));
  mkdirSync(join(projectRoot, ".sf"), { recursive: true });
  return projectRoot;
}

function cleanup(projectRoot: string): void {
  rmSync(projectRoot, { recursive: true, force: true });
}

test("detectProjectRag finds a project-rag server even when the MCP name is generic", () => {
  const projectRoot = makeProject();
  try {
    writeFileSync(
      join(projectRoot, ".mcp.json"),
      `${JSON.stringify({
        mcpServers: {
          project: {
            command: "/opt/project-rag/target/release/project-rag",
          },
        },
      }, null, 2)}\n`,
      "utf-8",
    );

    const detection = detectProjectRag(projectRoot);
    assert.equal(detection.status, "configured");
    assert.equal(detection.serverName, "project");
    assert.match(detection.command ?? "", /project-rag/);
  } finally {
    cleanup(projectRoot);
  }
});

test("detectProjectRag honors explicit server preference and disabled mode", () => {
  const projectRoot = makeProject();
  try {
    writeFileSync(
      join(projectRoot, ".mcp.json"),
      `${JSON.stringify({
        mcpServers: {
          code: {
            command: "/bin/project-rag",
          },
        },
      }, null, 2)}\n`,
      "utf-8",
    );

    assert.equal(detectProjectRag(projectRoot, { project_rag_server: "code" }).serverName, "code");
    assert.equal(detectProjectRag(projectRoot, { project_rag: "off" }).status, "disabled");
  } finally {
    cleanup(projectRoot);
  }
});

test("ensureProjectRagMcpConfig preserves existing servers and writes project-rag", () => {
  const projectRoot = makeProject();
  try {
    writeFileSync(
      join(projectRoot, ".mcp.json"),
      `${JSON.stringify({
        mcpServers: {
          existing: {
            command: "npx",
            args: ["other-mcp"],
          },
        },
      }, null, 2)}\n`,
      "utf-8",
    );

    const result = ensureProjectRagMcpConfig(projectRoot, {
      ...process.env,
      SF_PROJECT_RAG_BIN: "/tmp/project-rag",
      RUST_LOG: "warn",
    });
    assert.equal(result.status, "updated");
    assert.equal(result.serverName, PROJECT_RAG_MCP_SERVER_NAME);

    const parsed = JSON.parse(readFileSync(result.configPath, "utf-8")) as {
      mcpServers?: Record<string, { command?: string; args?: string[]; env?: Record<string, string> }>;
    };
    assert.deepEqual(parsed.mcpServers?.existing, {
      command: "npx",
      args: ["other-mcp"],
    });
    assert.equal(parsed.mcpServers?.[PROJECT_RAG_MCP_SERVER_NAME]?.command, "/tmp/project-rag");
    assert.equal(parsed.mcpServers?.[PROJECT_RAG_MCP_SERVER_NAME]?.env?.RUST_LOG, "warn");
  } finally {
    cleanup(projectRoot);
  }
});

test("resolveProjectRagBinaryForProject finds a vendored release binary", () => {
  const projectRoot = makeProject();
  try {
    const sourceDir = join(projectRoot, "vendor", "project-rag");
    const binaryPath = join(sourceDir, "target", "release", process.platform === "win32" ? "project-rag.exe" : "project-rag");
    mkdirSync(join(sourceDir, "target", "release"), { recursive: true });
    writeFileSync(join(sourceDir, "Cargo.toml"), '[package]\nname = "project-rag"\n', "utf-8");
    writeFileSync(binaryPath, "", "utf-8");

    assert.equal(findProjectRagSourceDir(projectRoot), sourceDir);
    assert.equal(resolveProjectRagBinaryForProject(projectRoot, { PATH: "" }), binaryPath);
  } finally {
    cleanup(projectRoot);
  }
});

test("ensureProjectRagMcpConfig uses vendored release binary when available", () => {
  const projectRoot = makeProject();
  try {
    const sourceDir = join(projectRoot, "vendor", "project-rag");
    const binaryPath = join(sourceDir, "target", "release", process.platform === "win32" ? "project-rag.exe" : "project-rag");
    mkdirSync(join(sourceDir, "target", "release"), { recursive: true });
    writeFileSync(join(sourceDir, "Cargo.toml"), '[package]\nname = "project-rag"\n', "utf-8");
    writeFileSync(binaryPath, "", "utf-8");

    const result = ensureProjectRagMcpConfig(projectRoot, { PATH: "" });
    const parsed = JSON.parse(readFileSync(result.configPath, "utf-8")) as {
      mcpServers?: Record<string, { command?: string }>;
    };
    assert.equal(parsed.mcpServers?.[PROJECT_RAG_MCP_SERVER_NAME]?.command, binaryPath);
  } finally {
    cleanup(projectRoot);
  }
});

test("resolveProjectRagBuildJobs caps project-rag builds by default and supports overrides", () => {
  assert.equal(resolveProjectRagBuildJobs({}), "2");
  assert.equal(resolveProjectRagBuildJobs({ CARGO_BUILD_JOBS: "4" }), "4");
  assert.equal(resolveProjectRagBuildJobs({ CARGO_BUILD_JOBS: "4", SF_PROJECT_RAG_BUILD_JOBS: "1" }), "1");
  assert.equal(resolveProjectRagBuildJobs({ SF_PROJECT_RAG_BUILD_JOBS: "nope" }), "2");
});

test("formatProjectRagStatus reports source-only project-rag as not operational", () => {
  const projectRoot = makeProject();
  try {
    const sourceDir = join(projectRoot, "vendor", "project-rag");
    mkdirSync(sourceDir, { recursive: true });
    writeFileSync(join(sourceDir, "Cargo.toml"), '[package]\nname = "project-rag"\n', "utf-8");

    const status = formatProjectRagStatus(projectRoot);
    assert.match(status, /Source:/);
    assert.match(status, /release binary is missing/i);
    assert.match(status, /\/sf codebase rag build/i);
  } finally {
    cleanup(projectRoot);
  }
});

test("buildCodeIntelligenceContextBlock injects project-rag usage guidance when configured", () => {
  const projectRoot = makeProject();
  try {
    writeFileSync(
      join(projectRoot, ".mcp.json"),
      `${JSON.stringify({
        mcpServers: {
          "project-rag": {
            command: "/tmp/project-rag",
          },
        },
      }, null, 2)}\n`,
      "utf-8",
    );

    const block = buildCodeIntelligenceContextBlock(projectRoot);
    assert.match(block, /PROJECT CODE INTELLIGENCE/);
    assert.match(block, /Project RAG: configured/);
    assert.match(block, /query_codebase/);
    assert.match(block, /search_by_filters/);
    assert.match(block, /If any Project RAG tool is missing or fails/);
  } finally {
    cleanup(projectRoot);
  }
});
