/**
 * Dinero en céntimos enteros (bigint) para no perder precisión con decimales flotantes.
 * La base de datos guarda Decimal(12,2); aquí se convierte a/desde texto "24900.50".
 */

export class MoneyFormatError extends Error {
  constructor(value: string) {
    super(`Monto inválido: "${value}"`);
    this.name = "MoneyFormatError";
  }
}

const DECIMAL_2 = /^(\d{1,10})(?:\.(\d{1,2}))?$/;

/** "24900" | "24900.5" | "24900.50" → 2490050n. Rechaza negativos y más de 2 decimales. */
export function toCents(value: string): bigint {
  const match = DECIMAL_2.exec(value.trim());
  if (!match) throw new MoneyFormatError(value);
  const units = match[1] ?? "0";
  const fraction = (match[2] ?? "").padEnd(2, "0");
  return BigInt(units) * 100n + BigInt(fraction);
}

/** 2490050n → "24900.50" */
export function fromCents(cents: bigint): string {
  const negative = cents < 0n;
  const abs = negative ? -cents : cents;
  const units = abs / 100n;
  const fraction = (abs % 100n).toString().padStart(2, "0");
  return `${negative ? "-" : ""}${units}.${fraction}`;
}

/**
 * Porcentaje con hasta 2 decimales → centésimas de punto porcentual.
 * "3.5" → 350n (3.50 %). Rango 0–100.
 */
export function toPercentHundredths(value: string): bigint {
  const hundredths = toCents(value);
  if (hundredths > 10_000n) throw new MoneyFormatError(value);
  return hundredths;
}

/** Redondeo "half up" de una división entera positiva. */
export function divideRoundHalfUp(numerator: bigint, denominator: bigint): bigint {
  if (denominator <= 0n) throw new RangeError("denominator must be positive");
  return (numerator * 2n + denominator) / (denominator * 2n);
}

/** Porcentaje de un monto en céntimos: (base × pct) redondeado al céntimo. */
export function percentageOf(baseCents: bigint, percentHundredths: bigint): bigint {
  return divideRoundHalfUp(baseCents * percentHundredths, 10_000n);
}
