package repository import ( "context" "strings" "github.com/jackc/pgx/v5/pgxpool" "financeiro-carvalho/internal/model" ) type TransactionRepository interface { IsDuplicate(ctx context.Context, date, description string, amount float64) (bool, error) IsExternalIDKnown(ctx context.Context, externalID string) (bool, error) BulkInsert(ctx context.Context, rows []model.ImportRow) (int, error) SaveImportLog(ctx context.Context, filename, format string, imported, duplicates, errors int) error } type transactionRepo struct{ db *pgxpool.Pool } func NewTransactionRepository(db *pgxpool.Pool) TransactionRepository { return &transactionRepo{db: db} } // normalizeDesc lowercases and collapses whitespace for fuzzy dedup. func normalizeDesc(s string) string { return strings.ToLower(strings.Join(strings.Fields(s), " ")) } func (r *transactionRepo) IsDuplicate(ctx context.Context, date, description string, amount float64) (bool, error) { var count int err := r.db.QueryRow(ctx, ` SELECT COUNT(*) FROM transactions WHERE date = $1 AND amount = $2 AND LOWER(description) = $3`, date, amount, normalizeDesc(description)).Scan(&count) return count > 0, err } func (r *transactionRepo) IsExternalIDKnown(ctx context.Context, externalID string) (bool, error) { if externalID == "" { return false, nil } var count int err := r.db.QueryRow(ctx, ` SELECT COUNT(*) FROM transactions WHERE external_id = $1`, externalID).Scan(&count) return count > 0, err } func (r *transactionRepo) BulkInsert(ctx context.Context, rows []model.ImportRow) (int, error) { tx, err := r.db.Begin(ctx) if err != nil { return 0, err } defer tx.Rollback(ctx) count := 0 for _, row := range rows { if row.IsDuplicate { continue } var extID *string if row.ExternalID != "" { extID = &row.ExternalID } _, err := tx.Exec(ctx, ` INSERT INTO transactions (date, amount, description, type, source, external_id) VALUES ($1, $2, $3, $4, 'import', $5)`, row.Date, row.Amount, row.Description, row.Type, extID) if err != nil { return count, err } count++ } return count, tx.Commit(ctx) } func (r *transactionRepo) SaveImportLog(ctx context.Context, filename, format string, imported, duplicates, errors int) error { _, err := r.db.Exec(ctx, ` INSERT INTO import_logs (filename, format, imported_count, duplicate_count, error_count) VALUES ($1, $2, $3, $4, $5)`, filename, format, imported, duplicates, errors) return err }