package db import ( "context" "database/sql" "errors" "mind/internal/models" ) type Repo struct { db *sql.DB } func NewRepo(db *sql.DB) *Repo { return &Repo{db: db} } // Conversations func (r *Repo) CreateConversation(ctx context.Context, ownerID int64, title string) (int64, error) { res, err := r.db.ExecContext(ctx, `INSERT INTO conversations(owner_id,title) VALUES(?,?)`, ownerID, title) if err != nil { return 0, err } return res.LastInsertId() } func (r *Repo) ListConversations(ctx context.Context, ownerID int64) ([]models.Conversation, error) { rows, err := r.db.QueryContext(ctx, `SELECT id, owner_id, title, created_at FROM conversations WHERE owner_id=? ORDER BY id DESC`, ownerID) if err != nil { return nil, err } defer rows.Close() var out []models.Conversation for rows.Next() { var c models.Conversation if err := rows.Scan(&c.ID, &c.OwnerID, &c.Title, &c.Created); err != nil { return nil, err } out = append(out, c) } return out, nil } // Nodes & Edges func (r *Repo) CreateNode(ctx context.Context, convID int64, authorKind, content string) (int64, error) { res, err := r.db.ExecContext(ctx, `INSERT INTO nodes(conversation_id,author_kind,content) VALUES(?,?,?)`, convID, authorKind, content) if err != nil { return 0, err } return res.LastInsertId() } func (r *Repo) Link(ctx context.Context, parentID, childID int64) error { _, err := r.db.ExecContext(ctx, `INSERT INTO edges(parent_id,child_id) VALUES(?,?)`, parentID, childID) return err } func (r *Repo) GetNode(ctx context.Context, id int64) (models.Node, error) { var n models.Node err := r.db.QueryRowContext(ctx, `SELECT id,conversation_id,author_kind,content,created_at FROM nodes WHERE id=?`, id). Scan(&n.ID, &n.ConversationID, &n.AuthorKind, &n.Content, &n.Created) return n, err } // Branches func (r *Repo) CreateOrGetBranch(ctx context.Context, convID int64, name string, headNodeID int64) (models.Branch, error) { // Try insert _, _ = r.db.ExecContext(ctx, `INSERT INTO branches(conversation_id,name,head_node_id) VALUES(?,?,?)`, convID, name, headNodeID) return r.GetBranch(ctx, convID, name) } func (r *Repo) GetBranch(ctx context.Context, convID int64, name string) (models.Branch, error) { var b models.Branch err := r.db.QueryRowContext(ctx, `SELECT id,conversation_id,name,head_node_id FROM branches WHERE conversation_id=? AND name=?`, convID, name). Scan(&b.ID, &b.ConversationID, &b.Name, &b.HeadNodeID) return b, err } func (r *Repo) MoveBranchHead(ctx context.Context, branchID, newHeadID int64) error { _, err := r.db.ExecContext(ctx, `UPDATE branches SET head_node_id=? WHERE id=?`, newHeadID, branchID) return err } // Latest-parent walk for linearize func (r *Repo) LatestParent(ctx context.Context, childID int64) (models.Node, bool, error) { row := r.db.QueryRowContext(ctx, ` SELECT n.id,n.conversation_id,n.author_kind,n.content,n.created_at FROM edges e JOIN nodes n ON n.id=e.parent_id WHERE e.child_id=? ORDER BY n.created_at DESC LIMIT 1`, childID) var n models.Node if err := row.Scan(&n.ID, &n.ConversationID, &n.AuthorKind, &n.Content, &n.Created); err != nil { if errors.Is(err, sql.ErrNoRows) { return models.Node{}, false, nil } return models.Node{}, false, err } return n, true, nil } func (r *Repo) DeleteBranch(ctx context.Context, convID int64, name string) error { _, err := r.db.ExecContext(ctx, `DELETE FROM branches WHERE conversation_id=? AND name=?`, convID, name) return err } // ListBranches returns all branches for a conversation, newest first. func (r *Repo) ListBranches(ctx context.Context, convID int64) ([]models.Branch, error) { rows, err := r.db.QueryContext(ctx, ` SELECT id, conversation_id, name, head_node_id FROM branches WHERE conversation_id=? ORDER BY id DESC`, convID) if err != nil { return nil, err } defer rows.Close() var out []models.Branch for rows.Next() { var b models.Branch if err := rows.Scan(&b.ID, &b.ConversationID, &b.Name, &b.HeadNodeID); err != nil { return nil, err } out = append(out, b) } return out, nil }