import assert from "node:assert/strict";
import { createServer } from "node:http";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import { createFoundryAgentGateway } from "./foundry-agent-gateway.mjs";
import { createFoundryAgentStore } from "./foundry-agent-store.mjs";
import { handleFoundryMcpRequest } from "./foundry-mcp.mjs";

async function listen(server) {
  await new Promise((resolve, reject) => {
    server.once("error", reject);
    server.listen(0, "127.0.0.1", resolve);
  });
  const address = server.address();
  return `http://127.0.0.1:${address.port}`;
}

async function close(server) {
  await new Promise((resolve) => server.close(resolve));
}

async function json(response) {
  return await response.json();
}

test("one setup command provisions independent Foundry and Ontology MCP transports", async () => {
  const dataRoot = await mkdtemp(path.join(os.tmpdir(), "nodedc-foundry-agent-gateway-"));
  const installerPath = path.join(dataRoot, "installer.tgz");
  await writeFile(installerPath, "installer", "utf8");
  let ontologyAuthorization = "";
  const ontologyServer = createServer(async (request, response) => {
    ontologyAuthorization = String(request.headers.authorization || "");
    const chunks = [];
    for await (const chunk of request) chunks.push(chunk);
    const message = JSON.parse(Buffer.concat(chunks).toString("utf8"));
    const result = message.method === "tools/list"
      ? { tools: [{ name: "ontology_status" }, { name: "ontology_search" }] }
      : {
        protocolVersion: "2025-06-18",
        capabilities: { tools: { listChanged: false } },
        serverInfo: { name: "nodedc-ontology-core", version: "test" },
      };
    response.writeHead(200, { "content-type": "application/json", "mcp-protocol-version": "2025-06-18" });
    response.end(JSON.stringify({ jsonrpc: "2.0", id: message.id, result }));
  });
  const ontologyBase = await listen(ontologyServer);
  const store = createFoundryAgentStore({ dataRoot: path.join(dataRoot, "store") });
  const gateway = createFoundryAgentGateway({
    store,
    installerPackagePath: installerPath,
    ontologyCoreUrl: ontologyBase,
    ontologyCoreAccessToken: "platform-internal-token",
  });
  const operations = {
    status: async () => ({ ok: true }),
    listApplications: async () => ({ applications: [] }),
    getApplication: async () => ({ application: null }),
  };
  const foundryServer = createServer(async (request, response) => {
    const url = new URL(request.url || "/", `http://${request.headers.host}`);
    if (await gateway.handlePublicRequest(request, response, url)) return;
    if (await gateway.handleOntologyMcp(request, response, url)) return;
    if (url.pathname === "/api/mcp") {
      await handleFoundryMcpRequest(request, response, {
        getConfig: () => ({ capabilitySecret: "", mcpAllowedOrigins: [] }),
        operations,
        authenticateAgentToken: gateway.authenticateFoundryToken,
      });
      return;
    }
    if (await gateway.handleManagementRequest(request, response, url, {
      id: "user_root",
      email: "dcctouch@gmail.com",
      displayName: "DC SUDO",
    })) return;
    response.writeHead(404);
    response.end();
  });
  const foundryBase = await listen(foundryServer);
  try {
    const createdResponse = await fetch(`${foundryBase}/api/foundry-agent-api/agents`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ name: "Foundry Codex" }),
    });
    assert.equal(createdResponse.status, 201);
    const created = await json(createdResponse);

    const setupResponse = await fetch(`${foundryBase}/api/foundry-agent-api/agents/${created.agent.id}/setup-code`, { method: "POST" });
    assert.equal(setupResponse.status, 201);
    const setup = await json(setupResponse);
    assert.match(setup.install.command, /nodedc-foundry-codex-agent/);

    const code = setup.install.command.match(/--code\s+(fnd_setup_[A-Za-z0-9_-]+)/)?.[1];
    assert.ok(code);
    const redeemResponse = await fetch(`${foundryBase}/api/foundry-agent/v1/setup/redeem`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ code, deviceName: "Test Codex" }),
    });
    assert.equal(redeemResponse.status, 200);
    const redeemed = await json(redeemResponse);
    assert.equal(redeemed.foundry.serverName, "nodedc_module_foundry");
    assert.equal(redeemed.ontology.serverName, "nodedc_ontology");
    assert.equal(redeemed.ontology.readOnly, true);
    assert.notEqual(redeemed.foundry.token, redeemed.ontology.token);

    const foundryListResponse = await fetch(redeemed.foundry.mcpUrl, {
      method: "POST",
      headers: { authorization: `Bearer ${redeemed.foundry.token}`, "content-type": "application/json" },
      body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list", params: {} }),
    });
    assert.equal(foundryListResponse.status, 200);
    const foundryList = await json(foundryListResponse);
    assert.ok(foundryList.result.tools.some((tool) => tool.name === "foundry_create_application"));
    const removePinTool = foundryList.result.tools.find((tool) => tool.name === "foundry_remove_map_pin_binding");
    assert.deepEqual(removePinTool.inputSchema.required, ["applicationId", "pageId", "bindingId", "idempotencyKey"]);
    assert.equal(removePinTool.inputSchema.additionalProperties, false);
    const presentationTool = foundryList.result.tools.find((tool) => tool.name === "foundry_upsert_map_presentation_profile");
    assert.equal(presentationTool.inputSchema.properties.profile.additionalProperties, false);
    assert.ok(presentationTool.inputSchema.properties.profile.required.includes("facets"));
    assert.ok(presentationTool.inputSchema.properties.profile.required.includes("target"));
    assert.equal(Object.hasOwn(presentationTool.inputSchema.properties.profile.properties, "pin"), false);
    assert.equal(presentationTool.inputSchema.properties.profile.properties.label.properties.sizePx.minimum, 8);
    assert.equal(Object.hasOwn(presentationTool.inputSchema.properties.profile.properties.label.properties, "fontSizePx"), false);
    assert.deepEqual(presentationTool.inputSchema.properties.profile.properties.label.properties.mode.enum, ["subject_id", "attributes", "none"]);
    const subjectDetailTool = foundryList.result.tools.find((tool) => tool.name === "foundry_upsert_map_subject_detail_profile");
    assert.ok(subjectDetailTool);
    assert.equal(subjectDetailTool.inputSchema.properties.profile.additionalProperties, false);
    assert.deepEqual(subjectDetailTool.inputSchema.properties.profile.required, ["id", "version", "title", "semanticTypes", "defaultTabId", "tabs"]);
    assert.deepEqual(
      subjectDetailTool.inputSchema.properties.profile.properties.tabs.items.properties.sections.items.properties.fields.items.properties.format.enum,
      ["text", "number", "timestamp", "boolean", "coordinate", "signal_state", "movement_state", "string_list", "telemetry_readings"],
    );
    const mapSettingsTool = foundryList.result.tools.find((tool) => tool.name === "foundry_update_map_page_settings");
    assert.ok(mapSettingsTool);
    assert.equal(mapSettingsTool.inputSchema.properties.settings.additionalProperties, false);
    assert.equal(Object.hasOwn(mapSettingsTool.inputSchema.properties.settings.properties, "providerUrl"), false);
    const mapViewStateTool = foundryList.result.tools.find((tool) => tool.name === "foundry_save_map_page_view_state");
    assert.ok(mapViewStateTool);
    assert.equal(mapViewStateTool.inputSchema.properties.viewState.properties.inspectorOpenSections.maxItems, 1);
    assert.equal(mapViewStateTool.inputSchema.properties.viewState.properties.inspectorOpenSections.uniqueItems, true);
    const subjectStateSchema = mapViewStateTool.inputSchema.properties.viewState.properties.subjectStates.items;
    assert.deepEqual(subjectStateSchema.required, ["bindingId", "visible", "filters", "window"]);
    assert.equal(subjectStateSchema.properties.filters.additionalProperties.items.type, "string");
    assert.equal(subjectStateSchema.properties.window.properties.rect.additionalProperties, false);
    const bindingTool = foundryList.result.tools.find((tool) => tool.name === "foundry_upsert_map_data_product_binding");
    assert.equal(bindingTool.inputSchema.properties.binding.properties.displayName.maxLength, 120);
    assert.equal(bindingTool.inputSchema.properties.binding.properties.order.maximum, 10000);
    assert.ok(bindingTool.inputSchema.properties.binding.properties.subjectDetailProfileId);
    assert.ok(bindingTool.inputSchema.properties.binding.properties.aspectId);
    assert.ok(bindingTool.inputSchema.properties.binding.properties.joinToBindingId);

    const crossTokenResponse = await fetch(redeemed.ontology.mcpUrl, {
      method: "POST",
      headers: { authorization: `Bearer ${redeemed.foundry.token}`, "content-type": "application/json" },
      body: JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }),
    });
    assert.equal(crossTokenResponse.status, 401);

    const ontologyListResponse = await fetch(redeemed.ontology.mcpUrl, {
      method: "POST",
      headers: { authorization: `Bearer ${redeemed.ontology.token}`, "content-type": "application/json" },
      body: JSON.stringify({ jsonrpc: "2.0", id: 3, method: "tools/list", params: {} }),
    });
    assert.equal(ontologyListResponse.status, 200);
    const ontologyList = await json(ontologyListResponse);
    assert.deepEqual(ontologyList.result.tools.map((tool) => tool.name), ["ontology_status", "ontology_search"]);
    assert.equal(ontologyAuthorization, "Bearer platform-internal-token");

    const revokeResponse = await fetch(`${foundryBase}/api/foundry-agent-api/agents/${created.agent.id}/revoke`, { method: "POST" });
    assert.equal(revokeResponse.status, 200);
    const revokedFoundryResponse = await fetch(redeemed.foundry.mcpUrl, {
      method: "POST",
      headers: { authorization: `Bearer ${redeemed.foundry.token}`, "content-type": "application/json" },
      body: JSON.stringify({ jsonrpc: "2.0", id: 4, method: "tools/list", params: {} }),
    });
    const revokedOntologyResponse = await fetch(redeemed.ontology.mcpUrl, {
      method: "POST",
      headers: { authorization: `Bearer ${redeemed.ontology.token}`, "content-type": "application/json" },
      body: JSON.stringify({ jsonrpc: "2.0", id: 5, method: "tools/list", params: {} }),
    });
    assert.equal(revokedFoundryResponse.status, 401);
    assert.equal(revokedOntologyResponse.status, 401);
  } finally {
    await close(foundryServer);
    await close(ontologyServer);
    await rm(dataRoot, { recursive: true, force: true });
  }
});
