import { createHash } from "node:crypto";
export function publicOrigin() {
  return new URL(process.env.APP_URL || "http://localhost:3000").origin;
}
export function sameOrigin(request: Request) {
  return (
    request.headers.get("origin") === publicOrigin() &&
    request.headers.get("sec-fetch-site") !== "cross-site"
  );
}
export function safeReturnTo(value: string) {
  return /^\/administracao(?:[/?]|$)/.test(value) && !value.includes("\\")
    ? value
    : "/administracao";
}
export class BodyTooLarge extends Error {}
export async function boundedBody(request: Request, max: number) {
  if (Number(request.headers.get("content-length") || 0) > max)
    throw new BodyTooLarge();
  const reader = request.body?.getReader();
  if (!reader) return new Uint8Array();
  const chunks: Uint8Array[] = [];
  let size = 0;
  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      size += value.length;
      if (size > max) {
        await reader.cancel();
        throw new BodyTooLarge();
      }
      chunks.push(value);
    }
  } finally {
    reader.releaseLock();
  }
  const body = new Uint8Array(size);
  let offset = 0;
  for (const c of chunks) {
    body.set(c, offset);
    offset += c.length;
  }
  return body;
}
export async function boundedFormData(request: Request, max = 64 * 1024) {
  const body = await boundedBody(request, max);
  return new Response(body, {
    headers: { "content-type": request.headers.get("content-type") || "" },
  }).formData();
}
export async function boundedJson(request: Request, max = 64 * 1024) {
  return JSON.parse(new TextDecoder().decode(await boundedBody(request, max)));
}
// Um processo por instituição. Buckets limitados e eliminados ao expirar; proxy deve sobrescrever o header de IP.
const buckets = new Map<string, { count: number; until: number }>();
export function rateLimit(
  request: Request,
  scope: string,
  max: number,
  windowMs = 15 * 60 * 1000,
) {
  const now = Date.now();
  for (const [key, b] of buckets) if (b.until <= now) buckets.delete(key);
  const ip =
    process.env.TRUST_PROXY === "true"
      ? request.headers.get("x-real-ip") || "unknown"
      : "local";
  const key = createHash("sha256")
    .update(scope + ":" + ip)
    .digest("hex");
  const b = buckets.get(key) || { count: 0, until: now + windowMs };
  if (!buckets.has(key) && buckets.size >= 5000) return false;
  b.count++;
  buckets.set(key, b);
  return b.count <= max;
}
