package repository import ( "context" "fmt" ) // ReorderPrograms sets sort_order to 1..n in the given order (transactional). // Uses an intermediate bump so UNIQUE (sort_order) is never violated mid-reorder. func (s *Store) ReorderPrograms(ctx context.Context, orderedIDs []int64) error { tx, err := s.conn.Begin(ctx) if err != nil { return err } defer func() { _ = tx.Rollback(ctx) }() if _, err := tx.Exec(ctx, ` UPDATE programs SET sort_order = sort_order + $1, updated_at = CURRENT_TIMESTAMP`, lessonReorderSortBump); err != nil { return err } for i, id := range orderedIDs { tag, err := tx.Exec(ctx, `UPDATE programs SET sort_order = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2`, int32(i+1), id) if err != nil { return err } if tag.RowsAffected() == 0 { return fmt.Errorf("program id %d not found", id) } } return tx.Commit(ctx) } // ReorderCoursesInProgram sets sort_order for courses under programID (transactional). // Uses an intermediate bump so UNIQUE (program_id, sort_order) is never violated mid-reorder. func (s *Store) ReorderCoursesInProgram(ctx context.Context, programID int64, orderedIDs []int64) error { tx, err := s.conn.Begin(ctx) if err != nil { return err } defer func() { _ = tx.Rollback(ctx) }() if _, err := tx.Exec(ctx, ` UPDATE courses SET sort_order = sort_order + $1, updated_at = CURRENT_TIMESTAMP WHERE program_id = $2`, lessonReorderSortBump, programID); err != nil { return err } for i, id := range orderedIDs { tag, err := tx.Exec(ctx, ` UPDATE courses SET sort_order = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2 AND program_id = $3`, int32(i+1), id, programID) if err != nil { return err } if tag.RowsAffected() == 0 { return fmt.Errorf("course id %d not in program %d", id, programID) } } return tx.Commit(ctx) } // ReorderModulesInCourse sets sort_order for modules under courseID (transactional). // Uses an intermediate bump so UNIQUE (course_id, sort_order) is never violated mid-reorder. func (s *Store) ReorderModulesInCourse(ctx context.Context, courseID int64, orderedIDs []int64) error { tx, err := s.conn.Begin(ctx) if err != nil { return err } defer func() { _ = tx.Rollback(ctx) }() if _, err := tx.Exec(ctx, ` UPDATE modules SET sort_order = sort_order + $1, updated_at = CURRENT_TIMESTAMP WHERE course_id = $2`, lessonReorderSortBump, courseID); err != nil { return err } for i, id := range orderedIDs { tag, err := tx.Exec(ctx, ` UPDATE modules SET sort_order = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2 AND course_id = $3`, int32(i+1), id, courseID) if err != nil { return err } if tag.RowsAffected() == 0 { return fmt.Errorf("module id %d not in course %d", id, courseID) } } return tx.Commit(ctx) } // ReorderLessonsInModule sets sort_order to 1..n under moduleID (transactional). // Uses an intermediate bump so UNIQUE (module_id, sort_order) is never violated mid-reorder. func (s *Store) ReorderLessonsInModule(ctx context.Context, moduleID int64, orderedIDs []int64) error { tx, err := s.conn.Begin(ctx) if err != nil { return err } defer func() { _ = tx.Rollback(ctx) }() if _, err := tx.Exec(ctx, ` UPDATE lessons SET sort_order = sort_order + $1, updated_at = CURRENT_TIMESTAMP WHERE module_id = $2`, lessonReorderSortBump, moduleID); err != nil { return err } for i, id := range orderedIDs { tag, err := tx.Exec(ctx, ` UPDATE lessons SET sort_order = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2 AND module_id = $3`, int32(i+1), id, moduleID) if err != nil { return err } if tag.RowsAffected() == 0 { return fmt.Errorf("lesson id %d not in module %d", id, moduleID) } } return tx.Commit(ctx) }