import type { Context } from "hono"; import { prisma } from "../../../lib/prisma"; import { logger } from "../../../lib/logger"; function parseArgentineAmount(raw: string): number { const cleaned = raw.replace("$", "").replace(/,/g, "").trim(); return Number(cleaned); } function parsePeriods(raw: string): number[] { const inner = raw.replace(/^\{|\}$/g, "").trim(); if (!inner) return []; return inner.split(",").map(Number); } export async function importPeriodicExpensesHandler(c: Context) { const body = await c.req.parseBody(); const file = body["file"]; if (!file || !(file instanceof File)) { return c.json({ error: "No se envió ningún archivo" }, 400); } const text = await file.text(); const lines = text.trim().split("\n"); if (lines.length < 2) { return c.json({ error: "El archivo está vacío o solo tiene encabezados" }, 400); } let imported = 0; let skippedNoPeriods = 0; let skippedDuplicate = 0; const errors: string[] = []; for (let i = 1; i < lines.length; i++) { const line = lines[i].trim(); if (!line) continue; const parts = line.split("|"); if (parts.length < 5) { errors.push(`Línea ${i + 1}: formato inválido`); continue; } const description = parts[1].trim(); const defaultDueDay = Number(parts[2].trim()); const rawAmount = parts[3].trim(); const rawPeriods = parts[4].trim(); const periods = parsePeriods(rawPeriods); if (periods.length === 0) { skippedNoPeriods++; continue; } try { const existing = await prisma.periodicExpense.findFirst({ where: { description: { equals: description, mode: "insensitive" } }, }); if (existing) { skippedDuplicate++; continue; } const defaultAmount = parseArgentineAmount(rawAmount); await prisma.periodicExpense.create({ data: { description, defaultDueDay: isNaN(defaultDueDay) ? 1 : defaultDueDay, defaultAmount: isNaN(defaultAmount) ? 0 : defaultAmount, periods, }, }); imported++; } catch (err) { const message = err instanceof Error ? err.message : String(err); errors.push(`Línea ${i + 1} ("${description}"): ${message}`); logger.error({ err, description }, "Error importing periodic expense"); } } return c.json({ imported, skipped: skippedNoPeriods + skippedDuplicate, skippedNoPeriods, skippedDuplicate, errors: errors.length > 0 ? errors : undefined, }); }