import { type BillingCopy, DEFAULT_BILLING_COPY } from './copy' import type { BillingStateResponse } from './types' import { EMPTY_BILLING_VALUE } from './use-billing-state' export function clampAmount(raw: string, billing: Pick): string { const amount = parseAmount(raw) if (amount == null) { return '' } const min = parseAmount(billing.min_usd) const max = parseAmount(billing.max_usd) const clampedMin = min == null ? amount : Math.max(min, amount) const clamped = max == null ? clampedMin : Math.min(max, clampedMin) return formatAmountForRequest(clamped) } export function parseAmount(value?: null | number | string): null | number { if (typeof value === 'number') { return Number.isFinite(value) ? value : null } if (typeof value !== 'string') { return null } const parsed = Number(value.replace(/[$,\s]/g, '')) return Number.isFinite(parsed) && parsed > 0 ? parsed : null } export function formatAmountForRequest(value: number): string { return Number.isInteger(value) ? String(value) : value.toFixed(2).replace(/0+$/, '').replace(/\.$/, '') } export function initialAutoReloadAmount(...candidates: Array): string { for (const candidate of candidates) { const amount = parseAmount(candidate) if (amount != null) { return formatAmountForRequest(amount) } } return '' } export function validateAutoReloadInputs( thresholdRaw: string, reloadToRaw: string, bounds: Pick, copy: BillingCopy = DEFAULT_BILLING_COPY ): { error?: string; values?: { reloadTo: string; threshold: string } } { const threshold = validateBillingAmount(copy.threshold, thresholdRaw, bounds, copy) if (threshold.error || threshold.amount == null) { return { error: threshold.error } } const reloadTo = validateBillingAmount(copy.reloadLabel, reloadToRaw, bounds, copy) if (reloadTo.error || reloadTo.amount == null) { return { error: reloadTo.error } } if (reloadTo.amount <= threshold.amount) { return { error: copy.reloadGreater } } return { values: { reloadTo: formatAmountForRequest(reloadTo.amount), threshold: formatAmountForRequest(threshold.amount) } } } export function validateBillingAmount( label: string, raw: string, bounds: Pick, copy: BillingCopy = DEFAULT_BILLING_COPY ): { amount?: number; error?: string } { const cleaned = raw.trim().replace(/^\$/, '').trim() if (!cleaned || !/^\d+(\.\d{1,2})?$/.test(cleaned)) { return { error: copy.invalidAmount(label) } } const amount = Number(cleaned) if (!(amount > 0)) { return { error: copy.positiveAmount(label) } } const min = parseAmount(bounds.min_usd) if (min != null && amount < min) { return { error: copy.minAmount(label, formatMoney(min)) } } const max = parseAmount(bounds.max_usd) if (max != null && amount > max) { return { error: copy.maxAmount(label, formatMoney(max)) } } return { amount } } export function formatMoney(value?: null | number | string): string { const amount = parseAmount(value) if (amount == null) { return EMPTY_BILLING_VALUE } return new Intl.NumberFormat(undefined, { currency: 'USD', maximumFractionDigits: amount % 1 === 0 ? 0 : 2, minimumFractionDigits: amount % 1 === 0 ? 0 : 2, style: 'currency' }).format(amount) }