
import { decorateErrorPayloadForHttp, logErrorEnvelope } from "./error-envelope.ts";

const DEFAULT_BODY_LIMIT_BYTES = 1024 * 1024;

export function readBody(request, limitBytes = DEFAULT_BODY_LIMIT_BYTES) {
  const limit = Number.isInteger(limitBytes) && limitBytes > 0 ? limitBytes : DEFAULT_BODY_LIMIT_BYTES;

  return new Promise((resolve, reject) => {
    let body = "";
    request.setEncoding("utf8");

    request.on("data", (chunk) => {
      body += chunk;
      if (Buffer.byteLength(body, "utf8") > limit) {
        request.destroy(new Error(`request body exceeds ${limit} bytes`));
      }
    });

    request.on("end", () => resolve(body));
    request.on("error", reject);
  });
}

export function sendJson(response, statusCode, body) {
  if (response.headersSent || response.writableEnded) return false;
  const payload = decorateErrorPayloadForHttp(body, { response, statusCode });
  logErrorEnvelope(payload, { statusCode });
  const headers: Record<string, string> = {
    "content-type": "application/json; charset=utf-8",
    "cache-control": "no-store"
  };
  const retryAfterSeconds = retryAfterHeaderSeconds(payload);
  if (retryAfterSeconds !== null) headers["retry-after"] = String(retryAfterSeconds);
  response.writeHead(statusCode, headers);
  response.end(JSON.stringify(payload) + "\n");
  return true;
}

function retryAfterHeaderSeconds(payload) {
  const retryAfterMs = Number(payload?.error?.retryAfterMs ?? payload?.retryAfterMs);
  if (!Number.isFinite(retryAfterMs) || retryAfterMs <= 0) return null;
  return Math.max(1, Math.ceil(retryAfterMs / 1000));
}

export function sendRedirect(response, location, body = {}) {
  response.writeHead(302, {
    "content-type": "application/json; charset=utf-8",
    "cache-control": "no-store",
    location
  });
  response.end(JSON.stringify({ redirectTo: location, ...body }) + "\n");
}

export function getHeader(request, name) {
  const value = request.headers[name.toLowerCase()];
  if (Array.isArray(value)) return value[0];
  return value;
}

export function firstHeaderValue(request, name) {
  return getHeader(request, name);
}

export function truthyFlag(value) {
  return /^(?:1|true|yes|on|enabled)$/iu.test(String(value ?? "").trim());
}

export function safeTraceId(value) {
  const text = String(value ?? "").trim();
  return /^trc_[A-Za-z0-9_.:-]+$/u.test(text) ? text : null;
}

export function safeSessionId(value) {
  const text = String(value ?? "").trim();
  return /^ses_[A-Za-z0-9_.:-]+$/u.test(text) ? text : null;
}

export function safeConversationId(value) {
  const text = String(value ?? "").trim();
  return /^cnv_[A-Za-z0-9_.:-]+$/u.test(text) ? text : null;
}

export function safeOpaqueId(value) {
  const text = String(value ?? "").trim();
  return /^[A-Za-z0-9_.:-]{3,180}$/u.test(text) ? text : null;
}

export function parsePositiveInteger(value, fallback) {
  const parsed = Number.parseInt(value ?? "", 10);
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
}

export function positiveInteger(value, fallback) {
  const parsed = Number.parseInt(value ?? "", 10);
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
}

export function text(value) {
  if (value === null || value === undefined) return "";
  return String(value);
}
