package db import ( "context" "database/sql" "encoding/json" "time" ) type Collection struct { ID string `json:"id"` UserID string `json:"userId"` Name string `json:"name"` Description string `json:"description"` IsPublic bool `json:"isPublic"` ContextEnabled bool `json:"contextEnabled"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` Items []CollectionItem `json:"items,omitempty"` ItemCount int `json:"itemCount,omitempty"` } type CollectionItem struct { ID string `json:"id"` CollectionID string `json:"collectionId"` ItemType string `json:"itemType"` Title string `json:"title"` Content string `json:"content"` URL string `json:"url"` Metadata map[string]interface{} `json:"metadata"` CreatedAt time.Time `json:"createdAt"` SortOrder int `json:"sortOrder"` } type CollectionRepository struct { db *PostgresDB } func NewCollectionRepository(db *PostgresDB) *CollectionRepository { return &CollectionRepository{db: db} } func (r *CollectionRepository) Create(ctx context.Context, c *Collection) error { query := ` INSERT INTO collections (user_id, name, description, is_public, context_enabled) VALUES ($1, $2, $3, $4, $5) RETURNING id, created_at, updated_at ` return r.db.db.QueryRowContext(ctx, query, c.UserID, c.Name, c.Description, c.IsPublic, c.ContextEnabled, ).Scan(&c.ID, &c.CreatedAt, &c.UpdatedAt) } func (r *CollectionRepository) GetByID(ctx context.Context, id string) (*Collection, error) { query := ` SELECT id, user_id, name, description, is_public, context_enabled, created_at, updated_at, (SELECT COUNT(*) FROM collection_items WHERE collection_id = collections.id) as item_count FROM collections WHERE id = $1 ` var c Collection err := r.db.db.QueryRowContext(ctx, query, id).Scan( &c.ID, &c.UserID, &c.Name, &c.Description, &c.IsPublic, &c.ContextEnabled, &c.CreatedAt, &c.UpdatedAt, &c.ItemCount, ) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, err } return &c, nil } func (r *CollectionRepository) GetByUserID(ctx context.Context, userID string, limit, offset int) ([]*Collection, error) { query := ` SELECT id, user_id, name, description, is_public, context_enabled, created_at, updated_at, (SELECT COUNT(*) FROM collection_items WHERE collection_id = collections.id) as item_count FROM collections WHERE user_id = $1 ORDER BY updated_at DESC LIMIT $2 OFFSET $3 ` rows, err := r.db.db.QueryContext(ctx, query, userID, limit, offset) if err != nil { return nil, err } defer rows.Close() var collections []*Collection for rows.Next() { var c Collection if err := rows.Scan( &c.ID, &c.UserID, &c.Name, &c.Description, &c.IsPublic, &c.ContextEnabled, &c.CreatedAt, &c.UpdatedAt, &c.ItemCount, ); err != nil { return nil, err } collections = append(collections, &c) } return collections, nil } func (r *CollectionRepository) Update(ctx context.Context, c *Collection) error { query := ` UPDATE collections SET name = $2, description = $3, is_public = $4, context_enabled = $5, updated_at = NOW() WHERE id = $1 ` _, err := r.db.db.ExecContext(ctx, query, c.ID, c.Name, c.Description, c.IsPublic, c.ContextEnabled, ) return err } func (r *CollectionRepository) Delete(ctx context.Context, id string) error { _, err := r.db.db.ExecContext(ctx, "DELETE FROM collections WHERE id = $1", id) return err } func (r *CollectionRepository) AddItem(ctx context.Context, item *CollectionItem) error { metadataJSON, _ := json.Marshal(item.Metadata) query := ` INSERT INTO collection_items (collection_id, item_type, title, content, url, metadata, sort_order) VALUES ($1, $2, $3, $4, $5, $6, COALESCE((SELECT MAX(sort_order) + 1 FROM collection_items WHERE collection_id = $1), 0)) RETURNING id, created_at, sort_order ` return r.db.db.QueryRowContext(ctx, query, item.CollectionID, item.ItemType, item.Title, item.Content, item.URL, metadataJSON, ).Scan(&item.ID, &item.CreatedAt, &item.SortOrder) } func (r *CollectionRepository) GetItems(ctx context.Context, collectionID string) ([]CollectionItem, error) { query := ` SELECT id, collection_id, item_type, title, content, url, metadata, created_at, sort_order FROM collection_items WHERE collection_id = $1 ORDER BY sort_order ASC ` rows, err := r.db.db.QueryContext(ctx, query, collectionID) if err != nil { return nil, err } defer rows.Close() var items []CollectionItem for rows.Next() { var item CollectionItem var metadataJSON []byte if err := rows.Scan( &item.ID, &item.CollectionID, &item.ItemType, &item.Title, &item.Content, &item.URL, &metadataJSON, &item.CreatedAt, &item.SortOrder, ); err != nil { return nil, err } json.Unmarshal(metadataJSON, &item.Metadata) items = append(items, item) } return items, nil } func (r *CollectionRepository) RemoveItem(ctx context.Context, itemID string) error { _, err := r.db.db.ExecContext(ctx, "DELETE FROM collection_items WHERE id = $1", itemID) return err } func (r *CollectionRepository) GetCollectionContext(ctx context.Context, collectionID string) (string, error) { items, err := r.GetItems(ctx, collectionID) if err != nil { return "", err } var context string for _, item := range items { switch item.ItemType { case "search": context += "Previous search: " + item.Title + "\n" if item.Content != "" { context += "Summary: " + item.Content + "\n" } case "note": context += "User note: " + item.Content + "\n" case "url": context += "Saved URL: " + item.URL + " - " + item.Title + "\n" case "file": context += "Uploaded file: " + item.Title + "\n" if item.Content != "" { context += "Content: " + item.Content + "\n" } } context += "\n" } return context, nil }