// Shared zod preprocess helper for optional free-text fields (memo,
// description, ref_no, address, phone, etc.) across every /api/c/* route
// module. Extracted during Task 3 of docs/superpowers/plans/2026-08-01-ap.md
// (the "included sweep") from server/src/bills.ts's original blankToUndefined,
// which itself was a regression fix for a bug that also existed, unfixed
// at the time, in invoices.ts and payments.ts (see the sweep commit).
//
// Empty string / absent -> undefined, so an untouched or blanked optional
// text field ends up NULL in storage rather than an empty string. Trims
// FIRST, before checking for blankness — a whitespace-only input ("   ")
// must also collapse to undefined/NULL, not slip through as a stored empty
// string.
//
// The bug this fixes: the ORIGINAL pattern used across this codebase's
// earlier route modules was
//   z.preprocess((v) => (v === "" || v === undefined || v === null ? undefined : v),
//     z.string().trim().max(N).optional())
// — checking blankness on the RAW value, then trimming only INSIDE the
// schema. A whitespace-only input ("   ") is not === "", so it passed the
// preprocess step unchanged; the schema's own .trim() then reduced it to ""
// AFTER preprocess had already decided to keep it — so it was stored as a
// real, non-NULL empty string rather than collapsing to NULL like every
// other blank input. For a field with a partial unique index keyed on
// "IS NOT NULL" (e.g. bills.ref_no), this let two whitespace-only inputs
// collide with each other under that index, surfacing as a confusing
// duplicate-key 409 instead of either succeeding freely or naming the
// actual (blank) value. bills.ts/vendors.ts/customers.ts were fixed first;
// this sweep applies the identical fix to invoices.ts and payments.ts, and
// centralizes the one true implementation here so no fourth copy of this
// bug can be introduced by a future file that forgets the trim-before-check
// ordering.
export function blankToUndefined(v: unknown): unknown {
  if (typeof v === "string") {
    const trimmed = v.trim();
    return trimmed === "" ? undefined : trimmed;
  }
  return v === null ? undefined : v;
}
