/** Minimal RFC 4180-style CSV serializer. No external dep. */

function escapeField(value: unknown): string {
  if (value === null || value === undefined) return "";
  const str = typeof value === "string" ? value : String(value);
  if (str.includes(",") || str.includes('"') || str.includes("\n") || str.includes("\r")) {
    return `"${str.replace(/"/g, '""')}"`;
  }
  return str;
}

/**
 * Serializes an array of rows into a CSV string (with header row, LF line terminators).
 *
 * @param rows     records to serialize
 * @param columns  ordered list of keys to emit (also the header names)
 */
export function toCSV<T extends object>(
  rows: readonly T[],
  columns: ReadonlyArray<keyof T & string>,
): string {
  const header = columns.map((c) => escapeField(c)).join(",");
  const body = rows
    .map((row) =>
      columns.map((col) => escapeField((row as Record<string, unknown>)[col])).join(","),
    )
    .join("\n");
  return body.length > 0 ? `${header}\n${body}\n` : `${header}\n`;
}
