// Client-side mirror of server/src/invoices.ts's computeLineAmount() — AR
// Task 5's invoice line editor needs to show a live "amount" preview per line
// (description/qty/price -> amount) as the user types, and that preview must
// round exactly the way the server will when the invoice is actually saved.
// Same technique as server/src/invoices.ts and web/src/lib/money.ts: every
// step is BigInt integer arithmetic, never a parsed JS float (CLAUDE.md §2).
//
// amount = quantity * unit_price, rounded HALF-UP to the nearest cent — see
// server/src/invoices.ts's file header for the worked example (0.5 * 99.99 =
// 49.995 -> 50.00, not banker's rounding). Returns null while the inputs
// aren't yet a complete decimal number (still mid-keystroke, e.g. "12.") so
// callers can show nothing rather than a wrong intermediate amount.

const DECIMAL_RE = /^\d+(\.\d+)?$/;

function toMicros(value: string): bigint {
  const [wholeRaw, fracRaw = ""] = value.split(".");
  const frac = (fracRaw + "0000").slice(0, 4);
  return BigInt(wholeRaw || "0") * 10000n + BigInt(frac || "0");
}

function fromMicros(micros: bigint): string {
  const whole = micros / 10000n;
  const frac = (micros % 10000n).toString().padStart(4, "0");
  return `${whole}.${frac}`;
}

/**
 * Computes a NUMERIC(19,4)-shaped preview string from raw quantity/unit-price
 * input strings, or null if either isn't a complete non-negative decimal yet.
 * An empty quantity defaults to "1" (matches invoice_lines' own DEFAULT 1 and
 * the server's resolveLines()).
 */
export function computeLineAmountPreview(rawQuantity: string, rawUnitPrice: string): string | null {
  const quantity = rawQuantity.trim() === "" ? "1" : rawQuantity.trim();
  const unitPrice = rawUnitPrice.trim();
  if (!DECIMAL_RE.test(quantity) || !DECIMAL_RE.test(unitPrice)) return null;

  const quantityMicros = toMicros(quantity);
  const priceMicros = toMicros(unitPrice);
  const product = quantityMicros * priceMicros; // scaled 1e8
  const CENTS_DIVISOR = 1_000_000n; // 1e8 / 1e2 (cents)
  const HALF = CENTS_DIVISOR / 2n;
  const cents = (product + HALF) / CENTS_DIVISOR; // round-half-up
  return fromMicros(cents * 100n); // cents (1e2) back up to micros (1e4)
}

/**
 * Exact addition of two NUMERIC(19,4)-shaped strings (never Number()) — used
 * to sum the lines editor's running total from each line's computed preview.
 */
export function sumMoney(values: string[]): string {
  let total = 0n;
  for (const v of values) {
    const match = /^(-)?(\d+)\.(\d{4})$/.exec(v.trim());
    if (!match) continue;
    const [, sign, whole, frac] = match as unknown as [string, string | undefined, string, string];
    const micros = BigInt(whole + frac);
    total += sign ? -micros : micros;
  }
  const negative = total < 0n;
  const magnitude = negative ? -total : total;
  const digits = magnitude.toString().padStart(5, "0");
  const wholePart = digits.slice(0, -4);
  const fracPart = digits.slice(-4);
  return `${negative ? "-" : ""}${wholePart}.${fracPart}`;
}
