import "server-only";
import { ApplicationError } from "./application-record";
export const applicationReply = (body: unknown, status = 200) =>
  Response.json(body, {
    status,
    headers: {
      "Cache-Control": "no-store, private",
      "X-Content-Type-Options": "nosniff",
    },
  });
export async function applicationBody(
  request: Request,
  origin: string,
  maxBytes = 32768,
): Promise<unknown> {
  if (request.headers.get("origin") !== origin)
    throw new ApplicationError(403, "Please use this website to continue.");
  if (
    request.headers.get("content-type")?.split(";")[0].trim().toLowerCase() !==
    "application/json"
  )
    throw new ApplicationError(415, "Unsupported request format.");
  if (!request.body || Number(request.headers.get("content-length")) > maxBytes)
    throw new ApplicationError(413, "The request is too large.");
  const reader = request.body.getReader();
  let size = 0,
    body = "",
    expired = false;
  const decoder = new TextDecoder("utf-8", { fatal: true });
  const timer = setTimeout(() => {
    expired = true;
    void reader.cancel().catch(() => {});
  }, 5000);
  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      size += value.byteLength;
      if (size > maxBytes) {
        await reader.cancel();
        throw new ApplicationError(413, "The request is too large.");
      }
      body += decoder.decode(value, { stream: true });
    }
    if (expired) throw new Error("timeout");
    return JSON.parse(body + decoder.decode()) as unknown;
  } catch (error) {
    if (error instanceof ApplicationError) throw error;
    throw new ApplicationError(400, "The request could not be read.");
  } finally {
    clearTimeout(timer);
    reader.releaseLock();
  }
}
export async function applicationRoute(run: () => Promise<Response>) {
  try {
    return await run();
  } catch (error) {
    return applicationReply(
      {
        ok: false,
        message:
          error instanceof ApplicationError
            ? error.message
            : "This service is temporarily unavailable. Your draft is safe; please retry.",
      },
      error instanceof ApplicationError ? error.status : 503,
    );
  }
}
