export function createCoreDiscoveryClient({
  coreUrl,
  gatewayToken,
  timeoutMs = 5000,
  fetchImpl = fetch,
} = {}) {
  const endpoint = normalizeCoreEndpoint(coreUrl);
  if (typeof gatewayToken !== "string" || gatewayToken.length < 32) {
    throw new TypeError("device_gateway_core_token_invalid");
  }
  const normalizedTimeout = Number(timeoutMs);
  if (
    !Number.isSafeInteger(normalizedTimeout)
    || normalizedTimeout < 100
    || normalizedTimeout > 30_000
  ) {
    throw new TypeError("device_gateway_core_timeout_invalid");
  }
  if (typeof fetchImpl !== "function") {
    throw new TypeError("device_gateway_core_fetch_invalid");
  }

  return async function observeDiscovery(signal) {
    const response = await fetchImpl(endpoint, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${gatewayToken}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(signal),
      signal: AbortSignal.timeout(normalizedTimeout),
    });
    const body = await readBoundedJson(response, 32 * 1024);
    if (!response.ok || body?.ok !== true) {
      throw new Error("device_gateway_core_ingest_failed");
    }
    if (
      !body.discovery
      || body.discovery.lifecycleState !== "quarantine"
      || body.discovery.commandTransport !== "disabled"
    ) {
      throw new Error("device_gateway_core_ingest_contract_invalid");
    }
    return body.discovery;
  };
}

function normalizeCoreEndpoint(value) {
  let url;
  try {
    url = new URL(String(value || ""));
  } catch {
    throw new TypeError("device_gateway_core_url_invalid");
  }
  if (url.protocol !== "http:" || url.username || url.password) {
    throw new TypeError("device_gateway_core_url_invalid");
  }
  if (url.pathname !== "/" || url.search || url.hash) {
    throw new TypeError("device_gateway_core_url_invalid");
  }
  url.pathname = "/internal/v1/device-discoveries:observe";
  return url.toString();
}

async function readBoundedJson(response, maxBytes) {
  const text = await response.text();
  if (Buffer.byteLength(text, "utf8") > maxBytes) {
    throw new Error("device_gateway_core_response_too_large");
  }
  try {
    return JSON.parse(text);
  } catch {
    throw new Error("device_gateway_core_response_invalid");
  }
}
