import { clsx, type ClassValue } from 'clsx' import { twMerge } from 'tailwind-merge' import { FixedDebt, VariableDebt, CardPayment } from './types' /** * Combina clases de Tailwind CSS usando clsx y tailwind-merge * Permite combinar múltiples clases condicionalmente */ export function cn(...inputs: ClassValue[]): string { return twMerge(clsx(inputs)) } /** * Formatea un número como moneda (pesos argentinos/USD) * Ejemplo: 1500.50 -> "$ 1.500,50" */ export function formatCurrency(amount: number): string { const formatter = new Intl.NumberFormat('es-AR', { style: 'currency', currency: 'ARS', minimumFractionDigits: 2, maximumFractionDigits: 2, }) return formatter.format(amount) } /** * Formatea una fecha en formato legible en español * Ejemplo: "28 de enero de 2026" */ export function formatDate(date: string | Date): string { const d = typeof date === 'string' ? new Date(date) : date const formatter = new Intl.DateTimeFormat('es-AR', { day: 'numeric', month: 'long', year: 'numeric', }) return formatter.format(d) } /** * Formatea una fecha en formato corto * Ejemplo: "28/01/2026" */ export function formatShortDate(date: string | Date): string { const d = typeof date === 'string' ? new Date(date) : date const formatter = new Intl.DateTimeFormat('es-AR', { day: '2-digit', month: '2-digit', year: 'numeric', }) return formatter.format(d) } /** * Calcula los días hasta una fecha específica * Retorna un número negativo si la fecha ya pasó */ export function getDaysUntil(date: string | Date): number { const targetDate = typeof date === 'string' ? new Date(date) : date const today = new Date() // Reset hours to compare only dates const target = new Date(targetDate.getFullYear(), targetDate.getMonth(), targetDate.getDate()) const current = new Date(today.getFullYear(), today.getMonth(), today.getDate()) const diffTime = target.getTime() - current.getTime() const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) return diffDays } /** * Obtiene la próxima fecha para un día específico del mes * Si el día ya pasó este mes, devuelve el del mes siguiente */ export function getNextDateByDay(dayOfMonth: number): Date { const today = new Date() const currentYear = today.getFullYear() const currentMonth = today.getMonth() const currentDay = today.getDate() let targetYear = currentYear let targetMonth = currentMonth // Si el día ya pasó este mes, ir al siguiente mes if (currentDay > dayOfMonth) { targetMonth += 1 if (targetMonth > 11) { targetMonth = 0 targetYear += 1 } } // Ajustar si el día no existe en el mes objetivo (ej: 31 de febrero) const lastDayOfMonth = new Date(targetYear, targetMonth + 1, 0).getDate() const targetDay = Math.min(dayOfMonth, lastDayOfMonth) return new Date(targetYear, targetMonth, targetDay) } /** * Obtiene el nombre del mes en español * El mes debe ser 1-12 (enero = 1) */ export function getMonthName(month: number): string { const monthNames = [ 'enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre', ] if (month < 1 || month > 12) { throw new Error('El mes debe estar entre 1 y 12') } return monthNames[month - 1] } /** * Calcula el total de deudas fijas no pagadas */ export function calculateTotalFixedDebts(debts: FixedDebt[]): number { return debts .filter((debt) => !debt.isPaid) .reduce((total, debt) => total + debt.amount, 0) } /** * Calcula el total de deudas variables no pagadas */ export function calculateTotalVariableDebts(debts: VariableDebt[]): number { return debts .filter((debt) => !debt.isPaid) .reduce((total, debt) => total + debt.amount, 0) } /** * Calcula el total de pagos de tarjeta * Opcionalmente filtrados por cardId */ export function calculateCardPayments( payments: CardPayment[], cardId?: string ): number { const filteredPayments = cardId ? payments.filter((payment) => payment.cardId === cardId) : payments return filteredPayments.reduce((total, payment) => total + payment.amount, 0) } /** * Calcula la próxima fecha de cierre de tarjeta * Si el día de cierre ya pasó este mes, devuelve el del mes siguiente */ export function calculateNextClosingDate(closingDay: number): Date { return getNextDateByDay(closingDay) } /** * Calcula la próxima fecha de vencimiento de tarjeta * Si el día de vencimiento ya pasó este mes, devuelve el del mes siguiente */ export function calculateNextDueDate(dueDay: number): Date { return getNextDateByDay(dueDay) } /** * Calcula el porcentaje de utilización de una tarjeta de crédito * Retorna un valor entre 0 y 100 */ export function getCardUtilization(balance: number, limit: number): number { if (limit <= 0) { return 0 } const utilization = (balance / limit) * 100 return Math.min(Math.max(utilization, 0), 100) }