package services import ( "bookhoard/internal/database" "context" "encoding/json" "errors" "fmt" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" ) // Custom errors for saved filters var ErrFilterNotFound = errors.New("filter not found or access denied") type FiltersService struct { db *database.Queries } func NewFiltersService(db *database.Queries) *FiltersService { return &FiltersService{db: db} } // GetSavedFilters - Retrieve all saved filters for a user + resource type func (s *FiltersService) GetSavedFilters(ctx context.Context, userID uuid.UUID, resourceType string) ([]database.SavedFilters, error) { filters, err := s.db.GetSavedFilters(ctx, database.GetSavedFiltersParams{ UserID: pgtype.UUID{Bytes: userID, Valid: true}, ResourceType: resourceType, }) if err != nil { return nil, fmt.Errorf("failed to get saved filters: %w", err) } return filters, nil } // GetSavedFilterByID - Retrieve a single saved filter by ID func (s *FiltersService) GetSavedFilterByID(ctx context.Context, userID uuid.UUID, filterID uuid.UUID) (database.SavedFilters, error) { filter, err := s.db.GetSavedFilterByID(ctx, database.GetSavedFilterByIDParams{ ID: pgtype.UUID{Bytes: filterID, Valid: true}, UserID: pgtype.UUID{Bytes: userID, Valid: true}, }) if err != nil { return database.SavedFilters{}, ErrFilterNotFound } return filter, nil } // CreateSavedFilter - Create a new saved filter func (s *FiltersService) CreateSavedFilter(ctx context.Context, userID uuid.UUID, name string, resourceType string, filters map[string]string) (database.SavedFilters, error) { // Business logic: Validate filter name uniqueness per user + resource type existing, err := s.db.GetSavedFilters(ctx, database.GetSavedFiltersParams{ UserID: pgtype.UUID{Bytes: userID, Valid: true}, ResourceType: resourceType, }) if err == nil { for _, f := range existing { if f.Name == name { return database.SavedFilters{}, fmt.Errorf("filter with name '%s' already exists for this resource type", name) } } } // Convert filters map to JSONB ([]byte) filtersJSON, err := json.Marshal(filters) if err != nil { return database.SavedFilters{}, fmt.Errorf("failed to marshal filters: %w", err) } filter, err := s.db.CreateSavedFilter(ctx, database.CreateSavedFilterParams{ UserID: pgtype.UUID{Bytes: userID, Valid: true}, Name: name, ResourceType: resourceType, Filters: filtersJSON, }) if err != nil { return database.SavedFilters{}, fmt.Errorf("failed to create saved filter: %w", err) } return filter, nil } // UpdateSavedFilter - Update an existing saved filter func (s *FiltersService) UpdateSavedFilter(ctx context.Context, userID uuid.UUID, filterID uuid.UUID, name string, filters map[string]string) (database.SavedFilters, error) { // Business logic: Verify filter exists and belongs to user existing, err := s.db.GetSavedFilterByID(ctx, database.GetSavedFilterByIDParams{ ID: pgtype.UUID{Bytes: filterID, Valid: true}, UserID: pgtype.UUID{Bytes: userID, Valid: true}, }) if err != nil { return database.SavedFilters{}, fmt.Errorf("filter not found or access denied: %w", err) } // Business logic: Check name uniqueness (excluding current filter) allFilters, err := s.db.GetSavedFilters(ctx, database.GetSavedFiltersParams{ UserID: pgtype.UUID{Bytes: userID, Valid: true}, ResourceType: existing.ResourceType, }) if err == nil { for _, f := range allFilters { existingID := uuid.Must(uuid.FromBytes(f.ID.Bytes[:])) if f.Name == name && existingID != filterID { return database.SavedFilters{}, fmt.Errorf("filter with name '%s' already exists for this resource type", name) } } } // Convert filters to JSONB filtersJSON, err := json.Marshal(filters) if err != nil { return database.SavedFilters{}, fmt.Errorf("failed to marshal filters: %w", err) } updated, err := s.db.UpdateSavedFilter(ctx, database.UpdateSavedFilterParams{ ID: pgtype.UUID{Bytes: filterID, Valid: true}, UserID: pgtype.UUID{Bytes: userID, Valid: true}, Name: name, Filters: filtersJSON, }) if err != nil { return database.SavedFilters{}, fmt.Errorf("failed to update saved filter: %w", err) } return updated, nil } // DeleteSavedFilter - Delete a saved filter func (s *FiltersService) DeleteSavedFilter(ctx context.Context, userID uuid.UUID, filterID uuid.UUID) error { _, err := s.db.DeleteSavedFilter(ctx, database.DeleteSavedFilterParams{ ID: pgtype.UUID{Bytes: filterID, Valid: true}, UserID: pgtype.UUID{Bytes: userID, Valid: true}, }) if err != nil { return fmt.Errorf("failed to delete saved filter: %w", err) } return nil }