package repository import ( "context" "errors" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "financeiro-carvalho/internal/model" ) type RecurringRepository interface { List(ctx context.Context) ([]model.RecurringExpense, error) GetByID(ctx context.Context, id int) (*model.RecurringExpense, error) Create(ctx context.Context, in model.RecurringInput) (*model.RecurringExpense, error) Update(ctx context.Context, id int, in model.RecurringInput) (*model.RecurringExpense, error) Delete(ctx context.Context, id int) error IsIgnored(ctx context.Context, id int, month string) (bool, string, error) Ignore(ctx context.Context, id int, month, reason string) error Unignore(ctx context.Context, id int, month string) error } type recurringRepo struct{ db *pgxpool.Pool } func NewRecurringRepository(db *pgxpool.Pool) RecurringRepository { return &recurringRepo{db: db} } func (r *recurringRepo) List(ctx context.Context) ([]model.RecurringExpense, error) { rows, err := r.db.Query(ctx, ` SELECT id, name, expected_amount, day_of_month, category_id, active, created_at, updated_at FROM recurring_expenses ORDER BY name ASC`) if err != nil { return nil, err } defer rows.Close() var out []model.RecurringExpense for rows.Next() { var re model.RecurringExpense if err := rows.Scan(&re.ID, &re.Name, &re.ExpectedAmount, &re.DayOfMonth, &re.CategoryID, &re.Active, &re.CreatedAt, &re.UpdatedAt); err != nil { return nil, err } out = append(out, re) } return out, rows.Err() } func (r *recurringRepo) GetByID(ctx context.Context, id int) (*model.RecurringExpense, error) { var re model.RecurringExpense err := r.db.QueryRow(ctx, ` SELECT id, name, expected_amount, day_of_month, category_id, active, created_at, updated_at FROM recurring_expenses WHERE id = $1`, id). Scan(&re.ID, &re.Name, &re.ExpectedAmount, &re.DayOfMonth, &re.CategoryID, &re.Active, &re.CreatedAt, &re.UpdatedAt) if errors.Is(err, pgx.ErrNoRows) { return nil, ErrNotFound } return &re, err } func (r *recurringRepo) Create(ctx context.Context, in model.RecurringInput) (*model.RecurringExpense, error) { var re model.RecurringExpense err := r.db.QueryRow(ctx, ` INSERT INTO recurring_expenses (name, expected_amount, day_of_month, category_id) VALUES ($1, $2, $3, $4) RETURNING id, name, expected_amount, day_of_month, category_id, active, created_at, updated_at`, in.Name, in.ExpectedAmount, in.DayOfMonth, in.CategoryID). Scan(&re.ID, &re.Name, &re.ExpectedAmount, &re.DayOfMonth, &re.CategoryID, &re.Active, &re.CreatedAt, &re.UpdatedAt) return &re, err } func (r *recurringRepo) Update(ctx context.Context, id int, in model.RecurringInput) (*model.RecurringExpense, error) { var re model.RecurringExpense err := r.db.QueryRow(ctx, ` UPDATE recurring_expenses SET name = $1, expected_amount = $2, day_of_month = $3, category_id = $4, updated_at = NOW() WHERE id = $5 RETURNING id, name, expected_amount, day_of_month, category_id, active, created_at, updated_at`, in.Name, in.ExpectedAmount, in.DayOfMonth, in.CategoryID, id). Scan(&re.ID, &re.Name, &re.ExpectedAmount, &re.DayOfMonth, &re.CategoryID, &re.Active, &re.CreatedAt, &re.UpdatedAt) if errors.Is(err, pgx.ErrNoRows) { return nil, ErrNotFound } return &re, err } func (r *recurringRepo) Delete(ctx context.Context, id int) error { tag, err := r.db.Exec(ctx, `DELETE FROM recurring_expenses WHERE id = $1`, id) if err != nil { return err } if tag.RowsAffected() == 0 { return ErrNotFound } return nil } func (r *recurringRepo) IsIgnored(ctx context.Context, id int, month string) (bool, string, error) { var reason string err := r.db.QueryRow(ctx, `SELECT COALESCE(reason, '') FROM recurring_ignores WHERE recurring_id = $1 AND month = $2`, id, month).Scan(&reason) if errors.Is(err, pgx.ErrNoRows) { return false, "", nil } return err == nil, reason, err } func (r *recurringRepo) Ignore(ctx context.Context, id int, month, reason string) error { _, err := r.db.Exec(ctx, ` INSERT INTO recurring_ignores (recurring_id, month, reason) VALUES ($1, $2, $3) ON CONFLICT (recurring_id, month) DO UPDATE SET reason = $3`, id, month, reason) return err } func (r *recurringRepo) Unignore(ctx context.Context, id int, month string) error { _, err := r.db.Exec(ctx, `DELETE FROM recurring_ignores WHERE recurring_id = $1 AND month = $2`, id, month) return err }