import assert from "node:assert/strict";
import { once } from "node:events";
import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises";
import { createServer as createHttpServer } from "node:http";
import { createServer } from "node:net";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { spawn } from "node:child_process";

const foundryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const gatewayRoot = resolve(foundryRoot, "../platform/services/map-gateway");
const root = await mkdtemp(join(tmpdir(), "nodedc-foundry-platform-settings-smoke-"));
const gatewayPort = await freePort();
const foundryPort = await freePort();
const providerPort = await freePort();
const internalSecret = "foundry-gateway-admin-smoke-secret";
const providerToken = "cesium-private-token-must-not-reach-browser";
const ionReferer = "https://foundry.example.test/page-library/";
const adminSecretFile = join(root, "map-gateway-admin-secret");
let gateway;
let foundry;
let provider;

try {
  await writeFile(adminSecretFile, `${"a".repeat(64)}\n`, { mode: 0o640 });
  await chmod(adminSecretFile, 0o640);
  provider = createCesiumIonMock(providerToken);
  provider.listen(providerPort, "127.0.0.1");
  await once(provider, "listening");
  gateway = spawn(process.execPath, ["src/server.mjs"], {
    cwd: gatewayRoot,
    env: {
      ...process.env,
      PORT: String(gatewayPort),
      MAP_CACHE_DIR: join(root, "gateway-cache"),
      MAP_GATEWAY_ALLOW_ANONYMOUS: "true",
      MAP_CACHE_MODE: "readwrite",
      NODE_ENV: "test",
      CESIUM_ION_TOKEN: "",
      CESIUM_ION_API_BASE_URL: `http://127.0.0.1:${providerPort}/`,
      NODEDC_MAP_GATEWAY_ADMIN_SECRET_FILE: adminSecretFile,
    },
    stdio: ["ignore", "pipe", "pipe"],
  });
  await waitForService(gatewayPort, gateway, "gateway");

  foundry = spawn(process.execPath, ["server/catalog-server.mjs"], {
    cwd: foundryRoot,
    env: {
      ...process.env,
      NODE_ENV: "development",
      HOST: "127.0.0.1",
      PORT: String(foundryPort),
      FOUNDRY_RUNTIME_DIR: join(root, "foundry-runtime"),
      NODEDC_FOUNDRY_AUTH_REQUIRED: "false",
      NODEDC_MAP_GATEWAY_INTERNAL_URL: `http://127.0.0.1:${gatewayPort}`,
      NODEDC_INTERNAL_ACCESS_TOKEN: internalSecret,
      NODEDC_MAP_GATEWAY_ADMIN_SECRET_FILE: adminSecretFile,
      FOUNDRY_PUBLIC_URL: "https://foundry.example.test",
    },
    stdio: ["ignore", "pipe", "pipe"],
  });
  await waitForService(foundryPort, foundry, "foundry");

  const profile = await fetch(`http://127.0.0.1:${foundryPort}/api/session/profile`).then((response) => response.json());
  assert.equal(profile.access.role, "admin");

  const initial = await fetch(`http://127.0.0.1:${foundryPort}/api/platform-settings/cesium-ion`, { headers: { referer: ionReferer } });
  assert.equal(initial.status, 200);
  assert.equal((await initial.json()).configured, false);

  const saved = await fetch(`http://127.0.0.1:${foundryPort}/api/platform-settings/cesium-ion`, {
    method: "PUT",
    headers: { "content-type": "application/json", referer: ionReferer },
    body: JSON.stringify({ token: providerToken }),
  });
  const savedRaw = await saved.text();
  assert.equal(saved.status, 200);
  assert.equal(savedRaw.includes(providerToken), false);
  assert.equal(JSON.parse(savedRaw).configured, true);
  assert.equal(JSON.parse(savedRaw).verification, "verified");

  const status = await fetch(`http://127.0.0.1:${foundryPort}/api/platform-settings/cesium-ion`, { headers: { referer: ionReferer } });
  const statusRaw = await status.text();
  assert.equal(status.status, 200);
  assert.equal(statusRaw.includes(providerToken), false);
  assert.equal(JSON.parse(statusRaw).configured, true);
  assert.equal(JSON.parse(statusRaw).verification, "verified");

  const runtime = await fetch(`http://127.0.0.1:${foundryPort}/api/map/runtime-config`);
  assert.equal(runtime.status, 200);
  const runtimeBody = await runtime.json();
  assert.equal(runtimeBody.gatewayReady, true);
  assert.equal(runtimeBody.assetEndpointBase, "/api/map-gateway/api/map/ion/assets");

  const terrain = await fetch(`http://127.0.0.1:${foundryPort}/api/map-gateway/api/map/ion/assets/1/endpoint`, { headers: { referer: ionReferer } });
  const terrainRaw = await terrain.text();
  assert.equal(terrain.status, 200);
  assert.equal(terrainRaw.includes(providerToken), false);
  assert.equal(JSON.parse(terrainRaw).credentialMode, "gateway");
  const buildings = await fetch(`http://127.0.0.1:${foundryPort}/api/map-gateway/api/map/ion/assets/96188/endpoint`, { headers: { referer: ionReferer } });
  const buildingsRaw = await buildings.text();
  assert.equal(buildings.status, 200);
  assert.equal(buildingsRaw.includes(providerToken), false);
  assert.equal(JSON.parse(buildingsRaw).credentialMode, "gateway");

  const health = await fetch(`http://127.0.0.1:${foundryPort}/api/map-gateway/healthz`);
  assert.equal(health.status, 200);
  assert.equal((await health.json()).cache.persistent, true);
  console.log("ok: Foundry admin settings use the private signed Gateway path without returning the token");
} finally {
  await stop(foundry);
  await stop(gateway);
  if (provider?.listening) {
    provider.close();
    await once(provider, "close");
  }
  await rm(root, { recursive: true, force: true });
}

function createCesiumIonMock(expectedToken) {
  return createHttpServer((request, response) => {
    if (request.headers.authorization !== `Bearer ${expectedToken}`) {
      response.writeHead(401, { "content-type": "application/json" });
      response.end(JSON.stringify({ error: "unauthorized" }));
      return;
    }
    if (request.headers.referer !== ionReferer) {
      response.writeHead(401, { "content-type": "application/json" });
      response.end(JSON.stringify({ error: "referer_required" }));
      return;
    }
    const assetId = new URL(request.url || "/", "http://127.0.0.1").pathname.match(/^\/v1\/assets\/(\d+)\/endpoint$/)?.[1];
    const body = assetId === "1"
      ? { type: "TERRAIN", url: "https://assets.ion.cesium.com/1/", accessToken: "terrain-private-endpoint-key" }
      : assetId === "2"
      ? { type: "IMAGERY", externalType: "BING", options: { url: "https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial", key: "bing-private-endpoint-key", mapStyle: "Aerial" } }
      : assetId === "96188"
        ? { type: "3DTILES", url: "https://assets.ion.cesium.com/96188/", accessToken: "buildings-private-endpoint-key" }
        : null;
    if (!body) {
      response.writeHead(404, { "content-type": "application/json" });
      response.end(JSON.stringify({ error: "not_found" }));
      return;
    }
    response.writeHead(200, { "content-type": "application/json" });
    response.end(JSON.stringify(body));
  });
}

async function freePort() {
  const server = createServer();
  server.listen(0, "127.0.0.1");
  await once(server, "listening");
  const address = server.address();
  assert(address && typeof address === "object");
  const port = address.port;
  server.close();
  await once(server, "close");
  return port;
}

async function waitForService(port, child, label) {
  let output = "";
  child.stderr.on("data", (chunk) => { output += String(chunk); });
  for (let attempt = 0; attempt < 80; attempt += 1) {
    if (child.exitCode !== null) throw new Error(`${label}_exited:${child.exitCode}:${output}`);
    try {
      const response = await fetch(`http://127.0.0.1:${port}/healthz`);
      if (response.ok) return;
    } catch { /* service is still starting */ }
    await new Promise((resolve) => setTimeout(resolve, 50));
  }
  throw new Error(`${label}_start_timeout:${output}`);
}

async function stop(child) {
  if (child && !child.killed) {
    child.kill("SIGTERM");
    await once(child, "exit").catch(() => undefined);
  }
}
