// Money helpers for the review workbench's reconcile footer — Task 5 of
// docs/superpowers/plans/2026-08-01-bank-rec.md. All arithmetic is whole-cents
// BigInt, never a parsed JS float (CLAUDE.md §2), mirroring
// billPayments/openBillLines.ts's own shape but for a value that — unlike
// every other money entry field in this app — may legitimately be NEGATIVE
// (a line of credit, an overdrawn account; see server/src/bank.ts's own
// signedAmountSchema comment).

/** Same shape as server/src/bank.ts's signedAmountSchema: up to 2 decimal
 *  places, optional leading "-", no leading zeros (other than a bare "0"). */
const SIGNED_ENTERED_RE = /^-?(0|[1-9]\d*)(\.\d{1,2})?$/;

export function signedEnteredError(raw: string): string | null {
  const trimmed = raw.trim();
  if (trimmed === "") return "Enter the statement's ending balance.";
  if (!SIGNED_ENTERED_RE.test(trimmed)) return "Enter an amount like 1234.56 (or -1234.56).";
  return null;
}

/** Parses a raw, already-validated signed dollars string ("-1234.5",
 *  "1234") into whole cents as a BigInt. */
export function centsOfSignedEntered(raw: string): bigint {
  const trimmed = raw.trim();
  const negative = trimmed.startsWith("-");
  const unsigned = negative ? trimmed.slice(1) : trimmed;
  const [wholeRaw, fracRaw = ""] = unsigned.split(".");
  const frac = (fracRaw + "00").slice(0, 2);
  const cents = BigInt(wholeRaw || "0") * 100n + BigInt(frac);
  return negative ? -cents : cents;
}

/** Parses a NUMERIC(19,4) string exactly as Postgres returns it (e.g. from
 *  GET .../book-balance's bookBalance field) into whole cents, rounding
 *  half-up exactly like web/src/lib/money.ts's fmtMoney() so a displayed
 *  figure and this figure never disagree by a rounding hair. */
export function centsOfNumeric(numeric: string): bigint {
  const match = /^(-)?(\d+)\.(\d{4})$/.exec(numeric.trim());
  if (!match) return 0n;
  const [, sign, intPart, fracPart] = match as unknown as [string, string | undefined, string, string];
  const scaled = BigInt(intPart + fracPart);
  const cents = (scaled + 50n) / 100n;
  return sign ? -cents : cents;
}

/** Reshapes a NUMERIC(19,4) string (e.g. a statement's prefilled
 *  endingBalance) into the plain signed dollars-and-cents form the ending
 *  balance <input> edits ("1234.56" / "-1234.56") — exact zero-padding, no
 *  rounding, so this is safe even though fmtMoney-style rounding isn't
 *  needed here (CLAUDE.md §2). */
export function numericToSignedEntered(numeric: string): string {
  const match = /^(-)?(\d+)\.(\d{4})$/.exec(numeric.trim());
  if (!match) return numeric;
  const [, sign, intPart, fracPart] = match as unknown as [string, string | undefined, string, string];
  return `${sign ?? ""}${intPart}.${fracPart.slice(0, 2)}`;
}

/** Formats a (possibly negative) whole-cents BigInt as "$1,234.56" /
 *  "-$1,234.56". */
export function fmtCentsUsd(cents: bigint): string {
  const negative = cents < 0n;
  const magnitude = negative ? -cents : cents;
  const digits = magnitude.toString().padStart(3, "0");
  const whole = digits.slice(0, -2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  const frac = digits.slice(-2);
  return `${negative ? "-" : ""}$${whole}.${frac}`;
}

/**
 * The footer's live, plain-English difference — mirrors
 * server/src/errors.ts's own reconcile-mismatch wording exactly (positive
 * diff = the bank's ending balance exceeds the books; negative = the books
 * exceed the bank) so the live preview never disagrees with what a 409 from
 * the real POST .../reconcile would say. diffCents = endingBalanceCents -
 * bookBalanceCents, same convention server/src/bank.ts's own reconcile route
 * computes.
 */
export function reconcileDifferenceSentence(diffCents: bigint): string {
  if (diffCents === 0n) return "That matches — you're ready to reconcile.";
  const magnitude = fmtCentsUsd(diffCents < 0n ? -diffCents : diffCents);
  return diffCents > 0n
    ? `${magnitude} more came in at the bank than in your books.`
    : `${magnitude} more is in your books than came in at the bank.`;
}
