package repository import ( "context" "errors" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "financeiro-carvalho/internal/middleware" "financeiro-carvalho/internal/model" ) var ErrNotFound = errors.New("not found") type CategoryRepository interface { List(ctx context.Context) ([]model.Category, error) GetByID(ctx context.Context, id int) (*model.Category, error) Create(ctx context.Context, name, color string, isTax bool) (*model.Category, error) Update(ctx context.Context, id int, name, color string, isTax bool) (*model.Category, error) Delete(ctx context.Context, id int) error HasTransactions(ctx context.Context, id int) (bool, error) } type categoryRepo struct{ db *pgxpool.Pool } func NewCategoryRepository(db *pgxpool.Pool) CategoryRepository { return &categoryRepo{db: db} } func (r *categoryRepo) List(ctx context.Context) ([]model.Category, error) { pid := middleware.ProfileIDFromCtx(ctx) rows, err := r.db.Query(ctx, ` SELECT id, name, color, is_default, is_tax, created_at, updated_at FROM categories WHERE profile_id = $1 ORDER BY is_default DESC, name ASC`, pid) if err != nil { return nil, err } defer rows.Close() var out []model.Category for rows.Next() { var c model.Category if err := rows.Scan(&c.ID, &c.Name, &c.Color, &c.IsDefault, &c.IsTax, &c.CreatedAt, &c.UpdatedAt); err != nil { return nil, err } out = append(out, c) } return out, rows.Err() } func (r *categoryRepo) GetByID(ctx context.Context, id int) (*model.Category, error) { pid := middleware.ProfileIDFromCtx(ctx) var c model.Category err := r.db.QueryRow(ctx, ` SELECT id, name, color, is_default, is_tax, created_at, updated_at FROM categories WHERE id = $1 AND profile_id = $2`, id, pid). Scan(&c.ID, &c.Name, &c.Color, &c.IsDefault, &c.IsTax, &c.CreatedAt, &c.UpdatedAt) if errors.Is(err, pgx.ErrNoRows) { return nil, ErrNotFound } return &c, err } func (r *categoryRepo) Create(ctx context.Context, name, color string, isTax bool) (*model.Category, error) { pid := middleware.ProfileIDFromCtx(ctx) var c model.Category err := r.db.QueryRow(ctx, ` INSERT INTO categories (name, color, is_tax, profile_id) VALUES ($1, $2, $3, $4) RETURNING id, name, color, is_default, is_tax, created_at, updated_at`, name, color, isTax, pid). Scan(&c.ID, &c.Name, &c.Color, &c.IsDefault, &c.IsTax, &c.CreatedAt, &c.UpdatedAt) return &c, err } func (r *categoryRepo) Update(ctx context.Context, id int, name, color string, isTax bool) (*model.Category, error) { pid := middleware.ProfileIDFromCtx(ctx) var c model.Category err := r.db.QueryRow(ctx, ` UPDATE categories SET name = $1, color = $2, is_tax = $3, updated_at = NOW() WHERE id = $4 AND profile_id = $5 RETURNING id, name, color, is_default, is_tax, created_at, updated_at`, name, color, isTax, id, pid). Scan(&c.ID, &c.Name, &c.Color, &c.IsDefault, &c.IsTax, &c.CreatedAt, &c.UpdatedAt) if errors.Is(err, pgx.ErrNoRows) { return nil, ErrNotFound } return &c, err } func (r *categoryRepo) Delete(ctx context.Context, id int) error { pid := middleware.ProfileIDFromCtx(ctx) tag, err := r.db.Exec(ctx, `DELETE FROM categories WHERE id = $1 AND profile_id = $2`, id, pid) if err != nil { return err } if tag.RowsAffected() == 0 { return ErrNotFound } return nil } func (r *categoryRepo) HasTransactions(ctx context.Context, id int) (bool, error) { pid := middleware.ProfileIDFromCtx(ctx) var count int err := r.db.QueryRow(ctx, `SELECT COUNT(*) FROM transactions WHERE category_id = $1 AND profile_id = $2`, id, pid). Scan(&count) return count > 0, err }