package store import ( "context" "fmt" "github.com/google/uuid" "git.warky.dev/wdevs/amcs/internal/generatedmodels" thoughttypes "git.warky.dev/wdevs/amcs/internal/types" ) // LinkThoughtLearning creates or updates an association between a thought (by GUID) and a learning (by numeric ID). // If the pair already exists, the relation label is updated. func (db *DB) LinkThoughtLearning(ctx context.Context, thoughtGUID uuid.UUID, learningID int64, relation string) error { _, err := db.pool.Exec(ctx, ` insert into thought_learning_links (thought_id, learning_id, relation) select t.id, $2, $3 from thoughts t where t.guid = $1 on conflict (thought_id, learning_id) do update set relation = excluded.relation `, thoughtGUID, learningID, relation) if err != nil { return fmt.Errorf("link thought learning: %w", err) } return nil } // UnlinkThoughtLearning removes the association between a thought (by GUID) and a learning (by numeric ID). func (db *DB) UnlinkThoughtLearning(ctx context.Context, thoughtGUID uuid.UUID, learningID int64) error { _, err := db.pool.Exec(ctx, ` delete from thought_learning_links where thought_id = (select id from thoughts where guid = $1) and learning_id = $2 `, thoughtGUID, learningID) if err != nil { return fmt.Errorf("unlink thought learning: %w", err) } return nil } // GetLinkedLearnings returns all learnings explicitly associated with a thought (by GUID). func (db *DB) GetLinkedLearnings(ctx context.Context, thoughtGUID uuid.UUID) ([]thoughttypes.LinkedLearning, error) { rows, err := db.pool.Query(ctx, ` select l.id, l.guid, l.summary, l.details, l.category, l.area, l.status, l.priority, l.confidence, l.action_required, l.source_type, l.source_ref, l.project_id, l.related_thought_id, l.related_skill_id, l.reviewed_by, l.reviewed_at, l.duplicate_of_learning_id, l.supersedes_learning_id, l.tags::text[], l.created_at, l.updated_at, tll.relation, tll.created_at as linked_at from thought_learning_links tll join learnings l on l.id = tll.learning_id where tll.thought_id = (select id from thoughts where guid = $1) order by tll.created_at desc `, thoughtGUID) if err != nil { return nil, fmt.Errorf("query linked learnings: %w", err) } defer rows.Close() items := make([]thoughttypes.LinkedLearning, 0) for rows.Next() { var tags []string var linked thoughttypes.LinkedLearning var m generatedmodels.ModelPublicLearnings if err := rows.Scan( &m.ID, &m.GUID, &m.Summary, &m.Details, &m.Category, &m.Area, &m.Status, &m.Priority, &m.Confidence, &m.ActionRequired, &m.SourceType, &m.SourceRef, &m.ProjectID, &m.RelatedThoughtID, &m.RelatedSkillID, &m.ReviewedBy, &m.ReviewedAt, &m.DuplicateOfLearningID, &m.SupersedesLearningID, &tags, &m.CreatedAt, &m.UpdatedAt, &linked.Relation, &linked.CreatedAt, ); err != nil { return nil, fmt.Errorf("scan linked learning: %w", err) } linked.Learning = learningFromModel(m, tags) items = append(items, linked) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate linked learnings: %w", err) } return items, nil } // GetLinkedThoughtsForLearning returns all thoughts explicitly associated with a learning (by numeric ID). func (db *DB) GetLinkedThoughtsForLearning(ctx context.Context, learningID int64) ([]thoughttypes.LinkedLearningThought, error) { rows, err := db.pool.Query(ctx, ` select t.id, t.guid, t.content, t.metadata, t.project_id, t.archived_at, t.created_at, t.updated_at, tll.relation, tll.created_at as linked_at from thought_learning_links tll join thoughts t on t.id = tll.thought_id where tll.learning_id = $1 order by tll.created_at desc `, learningID) if err != nil { return nil, fmt.Errorf("query linked thoughts for learning: %w", err) } defer rows.Close() items := make([]thoughttypes.LinkedLearningThought, 0) for rows.Next() { var linked thoughttypes.LinkedLearningThought var m generatedmodels.ModelPublicThoughts if err := rows.Scan( &m.ID, &m.GUID, &m.Content, &m.Metadata, &m.ProjectID, &m.ArchivedAt, &m.CreatedAt, &m.UpdatedAt, &linked.Relation, &linked.CreatedAt, ); err != nil { return nil, fmt.Errorf("scan linked thought for learning: %w", err) } thought, err := thoughtFromModel(m) if err != nil { return nil, fmt.Errorf("map linked thought for learning: %w", err) } linked.Thought = thought items = append(items, linked) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate linked thoughts for learning: %w", err) } return items, nil }