package repository import ( "errors" "fmt" "time" "accounting-app/internal/models" "gorm.io/gorm" ) // Common repository errors var ( ErrPiggyBankNotFound = errors.New("piggy bank not found") ErrPiggyBankInUse = errors.New("piggy bank is in use and cannot be deleted") ) // PiggyBankRepository handles database operations for piggy banks type PiggyBankRepository struct { db *gorm.DB } // NewPiggyBankRepository creates a new PiggyBankRepository instance func NewPiggyBankRepository(db *gorm.DB) *PiggyBankRepository { return &PiggyBankRepository{db: db} } // Create creates a new piggy bank in the database func (r *PiggyBankRepository) Create(piggyBank *models.PiggyBank) error { if err := r.db.Create(piggyBank).Error; err != nil { return fmt.Errorf("failed to create piggy bank: %w", err) } return nil } // GetByID retrieves a piggy bank by its ID func (r *PiggyBankRepository) GetByID(userID uint, id uint) (*models.PiggyBank, error) { var piggyBank models.PiggyBank if err := r.db.Preload("LinkedAccount").Where("user_id = ?", userID).First(&piggyBank, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrPiggyBankNotFound } return nil, fmt.Errorf("failed to get piggy bank: %w", err) } return &piggyBank, nil } // GetAll retrieves all piggy banks for a user func (r *PiggyBankRepository) GetAll(userID uint) ([]models.PiggyBank, error) { var piggyBanks []models.PiggyBank if err := r.db.Preload("LinkedAccount").Where("user_id = ?", userID).Order("created_at DESC").Find(&piggyBanks).Error; err != nil { return nil, fmt.Errorf("failed to get piggy banks: %w", err) } return piggyBanks, nil } // Update updates an existing piggy bank in the database func (r *PiggyBankRepository) Update(piggyBank *models.PiggyBank) error { // First check if the piggy bank exists var existing models.PiggyBank if err := r.db.Where("user_id = ?", piggyBank.UserID).First(&existing, piggyBank.ID).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrPiggyBankNotFound } return fmt.Errorf("failed to check piggy bank existence: %w", err) } // Update the piggy bank if err := r.db.Save(piggyBank).Error; err != nil { return fmt.Errorf("failed to update piggy bank: %w", err) } return nil } // Delete deletes a piggy bank by its ID func (r *PiggyBankRepository) Delete(userID uint, id uint) error { // First check if the piggy bank exists var piggyBank models.PiggyBank if err := r.db.Where("user_id = ?", userID).First(&piggyBank, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrPiggyBankNotFound } return fmt.Errorf("failed to check piggy bank existence: %w", err) } // Delete the piggy bank (soft delete due to gorm.DeletedAt field) if err := r.db.Delete(&piggyBank).Error; err != nil { return fmt.Errorf("failed to delete piggy bank: %w", err) } return nil } // GetByType retrieves all piggy banks of a specific type for a user func (r *PiggyBankRepository) GetByType(userID uint, piggyBankType models.PiggyBankType) ([]models.PiggyBank, error) { var piggyBanks []models.PiggyBank if err := r.db.Preload("LinkedAccount").Where("user_id = ? AND type = ?", userID, piggyBankType).Order("created_at DESC").Find(&piggyBanks).Error; err != nil { return nil, fmt.Errorf("failed to get piggy banks by type: %w", err) } return piggyBanks, nil } // GetByLinkedAccountID retrieves all piggy banks linked to a specific account for a user func (r *PiggyBankRepository) GetByLinkedAccountID(userID, accountID uint) ([]models.PiggyBank, error) { var piggyBanks []models.PiggyBank if err := r.db.Preload("LinkedAccount").Where("user_id = ? AND linked_account_id = ?", userID, accountID).Order("created_at DESC").Find(&piggyBanks).Error; err != nil { return nil, fmt.Errorf("failed to get piggy banks by linked account: %w", err) } return piggyBanks, nil } // GetActiveGoals retrieves all piggy banks that haven't reached their target yet for a user func (r *PiggyBankRepository) GetActiveGoals(userID uint) ([]models.PiggyBank, error) { var piggyBanks []models.PiggyBank if err := r.db.Preload("LinkedAccount").Where("user_id = ? AND current_amount < target_amount", userID).Order("created_at DESC").Find(&piggyBanks).Error; err != nil { return nil, fmt.Errorf("failed to get active piggy banks: %w", err) } return piggyBanks, nil } // GetCompletedGoals retrieves all piggy banks that have reached their target for a user func (r *PiggyBankRepository) GetCompletedGoals(userID uint) ([]models.PiggyBank, error) { var piggyBanks []models.PiggyBank if err := r.db.Preload("LinkedAccount").Where("user_id = ? AND current_amount >= target_amount", userID).Order("created_at DESC").Find(&piggyBanks).Error; err != nil { return nil, fmt.Errorf("failed to get completed piggy banks: %w", err) } return piggyBanks, nil } // GetGoalsDueBy retrieves all piggy banks with target dates on or before the specified date for a user func (r *PiggyBankRepository) GetGoalsDueBy(userID uint, date time.Time) ([]models.PiggyBank, error) { var piggyBanks []models.PiggyBank if err := r.db.Preload("LinkedAccount").Where("user_id = ? AND target_date IS NOT NULL AND target_date <= ?", userID, date).Order("target_date ASC").Find(&piggyBanks).Error; err != nil { return nil, fmt.Errorf("failed to get piggy banks due by date: %w", err) } return piggyBanks, nil } // ExistsByID checks if a piggy bank with the given ID exists for a user func (r *PiggyBankRepository) ExistsByID(userID, id uint) (bool, error) { var count int64 if err := r.db.Model(&models.PiggyBank{}).Where("user_id = ? AND id = ?", userID, id).Count(&count).Error; err != nil { return false, fmt.Errorf("failed to check piggy bank existence: %w", err) } return count > 0, nil }