import compression from "compression";
import { type Request, type RequestHandler, type Response } from "express";

/**
 * Below this, compressing costs more than it saves.
 *
 * The default is 1KB; this is slightly higher because almost every response
 * under a few KB here is a small status or preference object where the CPU and
 * the extra headers are not worth it. The payloads that matter — host lists,
 * audit pages, fleet inventories — are orders of magnitude above this.
 */
const MIN_RESPONSE_BYTES = 2048;

/**
 * Streaming endpoints that must not be buffered.
 *
 * Compression holds bytes back to build a block, which is exactly wrong for a
 * response whose value is arriving incrementally: SSE heartbeats and download
 * streams would stall until the buffer filled or the request ended.
 */
function isStreamingResponse(res: Response): boolean {
  const contentType = String(res.getHeader("Content-Type") ?? "");
  return (
    contentType.includes("text/event-stream") ||
    contentType.includes("application/octet-stream")
  );
}

/**
 * gzip for JSON API responses.
 *
 * The host list is the reason this exists: it is one big JSON array whose rows
 * repeat the same ~77 keys, so it compresses about 69x. Without this an install
 * with a few thousand hosts ships tens of megabytes per refresh.
 *
 * Deliberately not brotli: it compresses better but costs noticeably more CPU
 * per response, and the difference matters far less than the 60x that gzip
 * already recovers.
 */
export function createCompressionMiddleware(): RequestHandler {
  return compression({
    threshold: MIN_RESPONSE_BYTES,
    filter: (req: Request, res: Response) => {
      // Lets a caller opt out explicitly, which is useful when debugging.
      if (req.headers["x-no-compression"]) return false;
      if (isStreamingResponse(res)) return false;
      return compression.filter(req, res);
    },
  });
}
