/**
 * Aritmética decimal de ponto fixo com BigInt, escala 1e8 (8 casas, como
 * DECIMAL(24,8) no banco). Evita erros de float em valores monetários.
 *
 * Internamente um valor é um BigInt = valor * 10^8. As funções recebem e
 * devolvem strings para integrar com o resto do código.
 */

const SCALE = 8n;
const FACTOR = 10n ** SCALE; // 100000000

/** Converte string decimal -> BigInt escalado. Lança em entrada inválida. */
export function parse(s: string): bigint {
  const trimmed = s.trim();
  const m = /^(-?)(\d*)(?:\.(\d*))?$/.exec(trimmed);
  if (!m || (m[2] === "" && (m[3] ?? "") === "")) {
    throw new Error(`Decimal inválido: ${JSON.stringify(s)}`);
  }
  const sign = m[1] === "-" ? -1n : 1n;
  const intPart = m[2] || "0";
  const fracRaw = m[3] ?? "";
  const frac = (fracRaw + "00000000").slice(0, 8); // pad/trunca p/ 8 casas
  return sign * (BigInt(intPart) * FACTOR + BigInt(frac));
}

/** BigInt escalado -> string normalizada (sem zeros à direita supérfluos). */
export function format(v: bigint): string {
  const neg = v < 0n;
  const abs = neg ? -v : v;
  const intPart = abs / FACTOR;
  const fracPart = abs % FACTOR;
  let frac = fracPart.toString().padStart(8, "0").replace(/0+$/, "");
  const sign = neg ? "-" : "";
  return frac ? `${sign}${intPart}.${frac}` : `${sign}${intPart}`;
}

export function add(a: bigint, b: bigint): bigint {
  return a + b;
}
export function sub(a: bigint, b: bigint): bigint {
  return a - b;
}
/** Multiplicação de dois valores escalados (reescala dividindo por 1e8). */
export function mul(a: bigint, b: bigint): bigint {
  return (a * b) / FACTOR;
}
export function abs(a: bigint): bigint {
  return a < 0n ? -a : a;
}
export function cmp(a: bigint, b: bigint): number {
  return a < b ? -1 : a > b ? 1 : 0;
}
export function max(...vals: bigint[]): bigint {
  return vals.reduce((m, v) => (v > m ? v : m));
}
export function min(...vals: bigint[]): bigint {
  return vals.reduce((m, v) => (v < m ? v : m));
}

/** Conveniências string-in/string-out. */
export function mulStr(a: string, b: string): string {
  return format(mul(parse(a), parse(b)));
}
export function eqStr(a: string, b: string): boolean {
  return parse(a) === parse(b);
}
