Rewrite inline image markup

Most of the code is taken from Core files, also you might have to modify it a bit as per your own requirement.

Here is the core file link, where I got the idea from: https://developer.wordpress.org/reference/functions/wp_make_content_images_responsive/

You need to take care of the open p tag just before the image.

Was in hurry, so instead of waiting to check what you’ve tried, and modifying that, I ended up creating this snippet quickly.

add_filter( 'the_content', 'wpse_220739_modify_images', 100 );

/**
 * Get all the image tags from content and modify them
 *
 * @param $content
 *
 * @return mixed
 */
function wpse_220739_modify_images( $content ) {
    if ( ! preg_match_all( '/<img [^>]+>/', $content, $matches ) ) {
        return $content;
    }

    $selected_images = $attachment_ids = array();

    foreach ( $matches[0] as $image ) {
        if ( preg_match( '/size-([a-z]+)/i', $image, $class_size ) && preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) && $image_size = $class_size[1] && $attachment_id = absint( $class_id[1] ) ) {
            /*
             * If exactly the same image tag is used more than once, overwrite it.
             * All identical tags will be replaced later with 'str_replace()'.
             */
            $selected_images[ $image ] = $attachment_id;
            // Overwrite the ID when the same image is included more than once.
            $attachment_ids[ $attachment_id ] = true;
        }
    }
    foreach ( $selected_images as $image => $attachment_id ) {
        $content = str_replace( $image, wpse_220739_modify_image_tag( $image, $attachment_id, $image_size ), $content );
    }

    return $content;
}

/**
 * Modifies the image tag, by prepending a figure tag and adding necessary classes
 *
 * @param $image
 * @param $attachment_id
 * @param $image_size
 *
 * @return mixed|string
 */
function wpse_220739_modify_image_tag( $image, $attachment_id, $image_size ) {
    $image_src = preg_match( '/src="https://wordpress.stackexchange.com/questions/220739/([^"]+)"https://wordpress.stackexchange.com/", $image, $match_src ) ? $match_src[1] : '';
    list( $image_src ) = explode( '?', $image_src );

    // Return early if we couldn't get the image source.
    if ( ! $image_src ) {
        return $image;
    }
    //Get attachment meta for sizes
    $size_large  = wp_get_attachment_image_src( $attachment_id, 'large' );
    $size_medium = wp_get_attachment_image_src( $attachment_id, 'medium' );

    $size_large  = $size_large ? $size_large[0] : '';
    $size_medium = $size_medium ? $size_medium[0] : '';

    //Check if the image already have a respective attribute
    if ( ! strpos( $image, 'data-large' ) && ! empty( $size_large ) ) {
        $attr = sprintf( ' data-large="%s"', esc_attr( $size_large ) );
    }

    if ( ! strpos( $image, 'data-medium' ) && ! empty( $size_medium ) ) {
        $attr .= sprintf( ' data-medium="%s"', esc_attr( $size_medium ) );
    }

    // Add 'data' attributes
    $image = preg_replace( '/<img ([^>]+?)[\/ ]*>/', '<img $1' . $attr . ' />', $image );
    //Append figure tag
    $r_image = sprintf( '<figure id="image-%d" class="size-%s">', $image_size, $attachment_id );
    $r_image .= $image . '</figure>';

    return $r_image;

}

You’d need to add another check, if a Image tag already have a surrounding <figure> tag or not, but your main issue of not being able to modify all the images in content should be fixed by this.

Updated: The second foreach loop needs to be outside (Of course, silly me for not noticing that), that should help.