import { timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";

import {
  assertSafeProjection,
  hashRestrictedIdentifier,
  normalizeDiscoverySignal,
  toSafeDiscoveryView,
} from "../../../packages/device-protocol-contract/src/index.mjs";

export function createControlCoreApp({
  repository,
  gatewayToken = "",
  identifierPepper = "",
  discoveryIngestEnabled = false,
} = {}) {
  if (!repository || typeof repository.health !== "function") {
    throw new TypeError("device_repository_required");
  }
  if (discoveryIngestEnabled) {
    if (typeof repository.upsertQuarantineDiscovery !== "function") {
      throw new TypeError("device_discovery_repository_required");
    }
    if (typeof gatewayToken !== "string" || gatewayToken.length < 32) {
      throw new TypeError("device_gateway_token_invalid");
    }
    if (typeof identifierPepper !== "string" || identifierPepper.length < 32) {
      throw new TypeError("device_identifier_pepper_invalid");
    }
  }

  const server = createServer(async (request, response) => {
    response.setHeader("Content-Type", "application/json; charset=utf-8");
    response.setHeader("Cache-Control", "no-store");
    response.setHeader("X-Content-Type-Options", "nosniff");

    try {
      const requestUrl = new URL(
        request.url || "/",
        `http://${request.headers.host || "127.0.0.1"}`,
      );

      if (request.method === "GET" && requestUrl.pathname === "/healthz") {
        const database = await repository.health();
        return writeJson(response, 200, {
          ok: true,
          service: "nodedc-device-control-core",
          database,
          discoveryIngest: discoveryIngestEnabled ? "enabled" : "disabled",
          commandTransport: "disabled",
        });
      }

      if (
        request.method === "POST"
        && requestUrl.pathname === "/internal/v1/device-discoveries:observe"
      ) {
        if (!discoveryIngestEnabled) {
          return writeJson(response, 404, {
            ok: false,
            error: "device_discovery_ingest_disabled",
          });
        }
        if (!matchesBearer(request.headers.authorization, gatewayToken)) {
          return writeJson(response, 401, {
            ok: false,
            error: "device_gateway_auth_required",
          });
        }

        const input = await readJsonBody(request, 32 * 1024);
        const signal = normalizeDiscoverySignal(input);
        const identifierDigest = hashRestrictedIdentifier(
          signal.identifier,
          identifierPepper,
        );
        const safeView = assertSafeProjection(toSafeDiscoveryView(signal));
        const discovery = await repository.upsertQuarantineDiscovery({
          identifierDigest,
          safeView,
        });
        return writeJson(response, discovery.created ? 201 : 200, {
          ok: true,
          created: discovery.created,
          discovery: assertSafeProjection(discovery.value),
        });
      }

      return writeJson(response, 404, {
        ok: false,
        error: "device_control_core_route_not_found",
      });
    } catch (error) {
      const status = Number(error?.statusCode || 400);
      return writeJson(
        response,
        Number.isInteger(status) && status >= 400 && status < 600
          ? status
          : 500,
        {
          ok: false,
          error: safeErrorCode(error),
        },
      );
    }
  });

  return server;
}

function matchesBearer(header, expected) {
  if (typeof header !== "string" || !header.startsWith("Bearer ")) return false;
  const actual = Buffer.from(header.slice("Bearer ".length), "utf8");
  const required = Buffer.from(expected, "utf8");
  return (
    actual.length === required.length
    && required.length > 0
    && timingSafeEqual(actual, required)
  );
}

async function readJsonBody(request, maxBytes) {
  const chunks = [];
  let size = 0;
  for await (const chunk of request) {
    size += chunk.length;
    if (size > maxBytes) {
      const error = new Error("device_request_body_too_large");
      error.statusCode = 413;
      throw error;
    }
    chunks.push(chunk);
  }
  if (size === 0) throw new TypeError("device_request_body_required");
  try {
    return JSON.parse(Buffer.concat(chunks).toString("utf8"));
  } catch {
    throw new TypeError("device_request_json_invalid");
  }
}

function writeJson(response, status, body) {
  response.statusCode = status;
  return response.end(`${JSON.stringify(body)}\n`);
}

function safeErrorCode(error) {
  const value = error instanceof Error ? error.message : "device_control_error";
  return /^[a-z0-9_:-]{1,128}$/.test(value)
    ? value
    : "device_control_error";
}
