package parser import ( "encoding/csv" "fmt" "io" "math" "strconv" "strings" "time" "financeiro-carvalho/internal/model" ) func ParseCSV(r io.Reader, m model.CSVMapping) ([]model.ImportRow, []string) { if m.DateFormat == "" { m.DateFormat = "02/01/2006" } if m.DecimalSeparator == "" { m.DecimalSeparator = "," } fieldSep := ',' if m.FieldSeparator == ";" { fieldSep = ';' } reader := csv.NewReader(r) reader.Comma = fieldSep reader.LazyQuotes = true reader.TrimLeadingSpace = true var rows []model.ImportRow var errs []string lineNum := 0 for { record, err := reader.Read() if err == io.EOF { break } lineNum++ if err != nil { errs = append(errs, fmt.Sprintf("linha %d: %v", lineNum, err)) continue } if lineNum == 1 && m.HasHeader { continue } maxCol := max3(m.DateColumn, m.AmountColumn, m.DescriptionColumn) if len(record) <= maxCol { errs = append(errs, fmt.Sprintf("linha %d: colunas insuficientes (%d)", lineNum, len(record))) continue } dateStr := strings.TrimSpace(record[m.DateColumn]) amtStr := strings.TrimSpace(record[m.AmountColumn]) desc := cleanDescription(record[m.DescriptionColumn]) t, err := time.Parse(m.DateFormat, dateStr) if err != nil { errs = append(errs, fmt.Sprintf("linha %d: data inválida %q", lineNum, dateStr)) continue } if m.DecimalSeparator == "," { amtStr = strings.ReplaceAll(amtStr, ".", "") amtStr = strings.ReplaceAll(amtStr, ",", ".") } amt, err := strconv.ParseFloat(amtStr, 64) if err != nil { errs = append(errs, fmt.Sprintf("linha %d: valor inválido %q", lineNum, amtStr)) continue } txType := "expense" if amt > 0 { txType = "income" } rows = append(rows, model.ImportRow{ Date: t.Format("2006-01-02"), Amount: math.Abs(amt), Description: desc, Type: txType, }) } return rows, errs } func max3(a, b, c int) int { if b > a { a = b } if c > a { a = c } return a }