import assert from "node:assert/strict";
import test from "node:test";
import { formatFailure, startCraftos, startServer, waitFor, type Server } from "./harness.js";

// Drives the MCP tools end to end: a real reactive mcp-computer server (libcloud +
// libmcpcomputer) on a real CraftOS-PC client, reached through the real gateway. The
// MCP app is driven in-process via inject() (no extra listener).
async function callTool(server: Server, name: string, args: Record<string, unknown> = {}): Promise<string> {
  const res = await server.mcpApp.inject({
    method: "POST",
    url: "/",
    payload: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name, arguments: args } },
  });
  const body = res.json<{ result?: { content?: { text?: string }[] }; error?: { message?: string } }>();
  if (body.error) {
    throw new Error(`tool ${name} returned a JSON-RPC error: ${body.error.message}`);
  }
  return body.result?.content?.[0]?.text ?? "";
}

test("MCP exec-lua / run-file / write-file / probe round-trip over the gateway", async () => {
  const server = await startServer();
  const craftos = startCraftos("mcp-server-client.lua", {
    computerId: 7,
    computerLabel: "base",
    shellArgs: [server.baseUrl, "-"],
  });
  try {
    await waitFor(() => server.registry.has("global", "7:base"), 12_000, "computer registered");

    assert.equal(await callTool(server, "probe-computers"), "ok from 7:base");

    const exec = await callTool(server, "exec-lua", { traposId: "7:base", code: "print('hi'); return 1 + 1" });
    assert.match(exec, /^computer: 7:base\nok: true/);
    assert.match(exec, /"value":2/); // return value serialized
    assert.match(exec, /\nhi$/); // captured print output

    const program = "local a, b = ...; print(a .. ':' .. b); return arg[1], arg[2], shell.getRunningProgram()";
    const write = await callTool(server, "write-file", {
      traposId: "7:base",
      path: "/mcp-it.lua",
      content: program,
    });
    assert.match(write, /ok: true/);
    assert.match(write, new RegExp(`bytes: ${program.length}`));

    const run = await callTool(server, "run-file", { traposId: "7:base", path: "/mcp-it.lua", args: ["install", "pkg"] });
    assert.match(run, /^computer: 7:base\nok: true/);
    assert.match(run, /"value":"install"/);
    assert.match(run, /"value":"pkg"/);
    assert.match(run, /"value":"\/mcp-it\.lua"/);
    assert.match(run, /\ninstall:pkg$/);

    // Read it back through exec-lua to prove the write actually landed.
    const verify = await callTool(server, "exec-lua", {
      traposId: "7:base",
      code: "local h = fs.open('/mcp-it.lua', 'r'); local c = h.readAll(); h.close(); return c",
    });
    assert.match(verify, /shell\.getRunningProgram/);

    // Unknown traposId is a transport (not_connected) failure, not a crash.
    const ghost = await callTool(server, "exec-lua", { traposId: "9:ghost", code: "return 1" });
    assert.match(ghost, /^computer: 9:ghost\nok: false\nerror: not_connected:/);
  } catch (error) {
    throw new Error(formatFailure(error instanceof Error ? error.message : String(error), craftos.snapshot()), {
      cause: error,
    });
  } finally {
    craftos.abort();
    await craftos.done.catch(() => undefined);
    await server.close();
  }
});
