Your first 2 steps indeed work, for the 3rd step you will have to use a custom function which only checks for the order status 'processing'.
The advantage of this custom function is that it is much faster and lighter compared to going through all existing orders.
So you get:
function has_bought_items( $user_id = 0, $product_ids = 0 ) {
// When empty, return false
if ( empty ( $product_ids ) ) return false;
global $wpdb;
$product_ids = is_array( $product_ids ) ? implode( ',', $product_ids ) : $product_ids;
$line_meta_value = $product_ids != 0 ? 'AND woim.meta_value IN (' . $product_ids . ')' : 'AND woim.meta_value != 0';
// Count the number of products
$count = $wpdb->get_var( "
SELECT COUNT(p.ID) FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS woi ON p.ID = woi.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim ON woi.order_item_id = woim.order_item_id
WHERE p.post_status IN ( 'wc-processing' )
AND pm.meta_key = '_customer_user'
AND pm.meta_value = '$user_id'
AND woim.meta_key IN ( '_product_id', '_variation_id' ) $line_meta_value
" );
// Return true if count is higher than 0 (or false)
return $count > 0 ? true : false;
}
function action_woocommerce_before_checkout_form() {
// Customer must be logged in
if ( ! is_user_logged_in() ) return;
// Get current user
$user = wp_get_current_user();
// Allowed user roles
$allowed_roles = array( 'administrator', 'customer' );
// Compare
if ( array_intersect( $allowed_roles, $user->roles ) ) {
// WC Cart NOT null
if ( ! is_null( WC()->cart ) ) {
// Initialize
$product_ids = array();
// Loop through cart contents
foreach ( WC()->cart->get_cart_contents() as $cart_item ) {
// Get product ID and push to array
$product_ids[] = $cart_item['variation_id'] > 0 ? $cart_item['variation_id'] : $cart_item['product_id'];
}
// Call function, and if true
if ( has_bought_items( $user->ID, $product_ids ) ) {
// Notice
wc_print_notice( __( 'You purchased this in the past. Buy again?', 'woocommerce' ), 'success' );
}
}
}
}
add_action( 'woocommerce_before_checkout_form', 'action_woocommerce_before_checkout_form' );
Result: a general message

Optional: Instead of displaying a general message, but showing this separately per product, you can use the woocommerce_checkout_cart_item_quantity
hook
So you would get:
function has_bought_items( $user_id = 0, $product_ids = 0 ) {
// When empty, return false
if ( empty ( $product_ids ) ) return false;
global $wpdb;
$product_ids = is_array( $product_ids ) ? implode( ',', $product_ids ) : $product_ids;
$line_meta_value = $product_ids != 0 ? 'AND woim.meta_value IN (' . $product_ids . ')' : 'AND woim.meta_value != 0';
// Count the number of products
$count = $wpdb->get_var( "
SELECT COUNT(p.ID) FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS woi ON p.ID = woi.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim ON woi.order_item_id = woim.order_item_id
WHERE p.post_status IN ( 'wc-processing' )
AND pm.meta_key = '_customer_user'
AND pm.meta_value = '$user_id'
AND woim.meta_key IN ( '_product_id', '_variation_id' ) $line_meta_value
" );
// Return true if count is higher than 0 (or false)
return $count > 0 ? true : false;
}
function filter_woocommerce_checkout_cart_item_quantity( $item_qty, $cart_item, $cart_item_key ) {
// Customer must be logged in
if ( ! is_user_logged_in() ) return;
// Get current user
$user = wp_get_current_user();
// Allowed user roles
$allowed_roles = array( 'administrator', 'customer' );
// Initialize
$message = '';
// Compare
if ( array_intersect( $allowed_roles, $user->roles ) ) {
// Get product id
$product_id = $cart_item['variation_id'] > 0 ? $cart_item['variation_id'] : $cart_item['product_id'];
// Call function, and if true
if ( has_bought_items( $user->ID, $product_id ) ) {
$message = '<p>' . __( 'You purchased this in the past. Buy again?', 'woocommerce' ) . '</p>';
}
}
// Return
return $item_qty . $message;
}
add_filter( 'woocommerce_checkout_cart_item_quantity', 'filter_woocommerce_checkout_cart_item_quantity', 10, 3 );
Result: show separately by product

Note: the has_bought_items()
function is based on Check if a user has purchased specific products in WooCommerce answer code
Related: Display message below product name on WooCommerce cart page if user has bought product before