package storage import ( "database/sql" "time" "github.com/thepeterstone/claudomator/internal/story" ) // CreateEpic inserts a new epic. Defaults Status to "OPEN" if unset. func (s *DB) CreateEpic(e *story.Epic) error { if e.Status == "" { e.Status = "OPEN" } now := time.Now().UTC() e.CreatedAt = now e.UpdatedAt = now _, err := s.db.Exec( `INSERT INTO epics (id, name, description, status, discovery_source, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)`, e.ID, e.Name, e.Description, e.Status, e.DiscoverySource, now, now, ) return err } // GetEpic retrieves an epic by ID. func (s *DB) GetEpic(id string) (*story.Epic, error) { row := s.db.QueryRow(`SELECT id, name, description, status, discovery_source, created_at, updated_at FROM epics WHERE id = ?`, id) return scanEpic(row) } // ListEpics returns epics, optionally filtered by status. Pass an empty // string to list all epics. func (s *DB) ListEpics(status string) ([]*story.Epic, error) { query := `SELECT id, name, description, status, discovery_source, created_at, updated_at FROM epics WHERE 1=1` var args []interface{} if status != "" { query += " AND status = ?" args = append(args, status) } query += " ORDER BY created_at DESC" rows, err := s.db.Query(query, args...) if err != nil { return nil, err } defer rows.Close() var epics []*story.Epic for rows.Next() { e, err := scanEpicRows(rows) if err != nil { return nil, err } epics = append(epics, e) } return epics, rows.Err() } // UpdateEpic updates an existing epic's name/description/status/discovery_source. // Mirrors UpdateProject's shape: no existence check, no status-machine validation. func (s *DB) UpdateEpic(e *story.Epic) error { now := time.Now().UTC() _, err := s.db.Exec( `UPDATE epics SET name = ?, description = ?, status = ?, discovery_source = ?, updated_at = ? WHERE id = ?`, e.Name, e.Description, e.Status, e.DiscoverySource, now, e.ID, ) if err != nil { return err } e.UpdatedAt = now return nil } func scanEpic(row scanner) (*story.Epic, error) { var e story.Epic var description sql.NullString var discoverySource sql.NullString if err := row.Scan(&e.ID, &e.Name, &description, &e.Status, &discoverySource, &e.CreatedAt, &e.UpdatedAt); err != nil { return nil, err } e.Description = description.String e.DiscoverySource = discoverySource.String return &e, nil } func scanEpicRows(rows *sql.Rows) (*story.Epic, error) { return scanEpic(rows) }