package store import ( "context" "fmt" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" ) // List is the projection of a list row. type List struct { ID uuid.UUID `json:"id"` Name string `json:"name"` OwnerID uuid.UUID `json:"owner_id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt *time.Time `json:"deleted_at,omitempty"` HLCTS int64 `json:"hlc_ts"` } // ListStore provides read/write access to the lists projection. type ListStore struct { pool *pgxpool.Pool } // NewListStore creates a ListStore backed by the given pool. func NewListStore(pool *pgxpool.Pool) *ListStore { return &ListStore{pool: pool} } // CreateList inserts a new list owned by ownerID. // In the MVP, list creation is a direct INSERT (no op required). // The caller is responsible for emitting a list_create op if desired for full // op-log coverage; for Phase C the REST endpoint does a direct insert. func (s *ListStore) CreateList(ctx context.Context, ownerID uuid.UUID, name string) (*List, error) { var l List err := s.pool.QueryRow(ctx, ` INSERT INTO lists (name, owner_id) VALUES ($1, $2) RETURNING id, name, owner_id, created_at, updated_at, hlc_ts`, name, ownerID, ).Scan(&l.ID, &l.Name, &l.OwnerID, &l.CreatedAt, &l.UpdatedAt, &l.HLCTS) if err != nil { return nil, fmt.Errorf("liststore: create list: %w", err) } return &l, nil } // GetLists returns all non-deleted lists owned by ownerID, newest first. func (s *ListStore) GetLists(ctx context.Context, ownerID uuid.UUID) ([]List, error) { rows, err := s.pool.Query(ctx, ` SELECT id, name, owner_id, created_at, updated_at, hlc_ts FROM lists WHERE owner_id = $1 AND deleted_at IS NULL ORDER BY created_at DESC`, ownerID, ) if err != nil { return nil, fmt.Errorf("liststore: get lists: %w", err) } defer rows.Close() var lists []List for rows.Next() { var l List if err := rows.Scan(&l.ID, &l.Name, &l.OwnerID, &l.CreatedAt, &l.UpdatedAt, &l.HLCTS); err != nil { return nil, fmt.Errorf("liststore: scan list row: %w", err) } lists = append(lists, l) } return lists, rows.Err() } // GetList returns a single non-deleted list, checking that userID is the owner. // Returns pgx.ErrNoRows if not found or not owned by userID. func (s *ListStore) GetList(ctx context.Context, listID, userID uuid.UUID) (*List, error) { var l List err := s.pool.QueryRow(ctx, ` SELECT id, name, owner_id, created_at, updated_at, hlc_ts FROM lists WHERE id = $1 AND owner_id = $2 AND deleted_at IS NULL`, listID, userID, ).Scan(&l.ID, &l.Name, &l.OwnerID, &l.CreatedAt, &l.UpdatedAt, &l.HLCTS) if err != nil { return nil, fmt.Errorf("liststore: get list: %w", err) } return &l, nil }