plugin development – WordPress Cron Scheduler is running function multiple times in succession

I have a plugin that allows a user to set a date to schedule a set of emails to get out. When they save the settings it creates a scheduled task set up to run weekly. That all seems to work as expected. I can view the scheduled job to run using the WP Crontrol plugin.

The problem is that once it hits its countdown, it seems to run the function around 8 times or so instead of just once, so it sends out 15 emails (one for each location) x 8.

I’m trying to figure out if i set this up wrong. I can post my construct and the method it calls.

suddath_send_reviews is the hook

public function __construct( $plugin_name, $version ) {
    
    $this->plugin_name = $plugin_name;
    $this->version     = $version;
    
    // Actions
    add_action( 'init', ( $this, 'create_reviews_location_taxonomy' ), 0 );
    add_action( 'admin_menu', ( $this, 'create_admin_menu_item' ) );
    add_action( 'admin_init', ( $this, 'update_general_settings' ) );
    // add_action( 'admin_menu', ( $this, 'add_tax_to_reviews' ) );
    add_action( 'site-reviews/review/created', ( $this, 'add_location_to_submitted_review' ), 10, 2 );
    add_action( 'wp_ajax_send_email_data', ( $this, 'get_reviews_one_location' ) );
    add_action( 'wp_ajax_nopriv_send_email_data', ( $this, 'get_reviews_one_location' ) );
    add_action( 'wp_ajax_send_all_email_data', ( $this, 'get_reviews_all_locations' ) );
    add_action( 'wp_ajax_nopriv_send_all_email_data', ( $this, 'get_reviews_all_locations' ) );
    add_action( 'admin_notices', ( $this, 'rr_updated_admin_notice' ));
    add_action( 'acf/init', ( $this, 'rr_acf_add_local_field_groups' ));
    
    // Filters
    add_filter( 'cron_schedules', ( $this, 'add_weekly_cron_schedule' ) );
    
    // get day/time options
    $day   = get_option( 'send_review_day' );
    $time  = get_option( 'send_review_time' );
    $am_pm = get_option( 'send_review_nightday' );
    
    wp_unschedule_hook( 'suddath_send_reviews' );
    
    // run scheduler based on options selected on plugin admin page
    // If it hasnt already been scheduled 
    if (
        !empty( $day ) &&
        !empty( $time ) &&
        !empty( $am_pm )
        ):
        $hour = explode( ':', $time )(0);
        if ( $am_pm === 'pm' ):
            $hour += 12;
            $time = "$hour:00:00";
        endif;
        
        // Add 5 hours  because cron defaults to UTC -5
        if ( ! wp_next_scheduled( 'suddath_send_reviews' ) ):
            
            wp_schedule_event(
                strtotime( "this $day $time +5 hours" ),
                get_option( 'send_review_frequency' ),
                'suddath_send_reviews'
            );
            // else:
                //  wp_unschedule_hook( 'suddath_send_reviews' );
                //  wp_schedule_event(
                    //      strtotime( "this $day $time +5 hours" ),
                    //      get_option( 'send_review_frequency' ),
                    //      'suddath_send_reviews'
                    //  );
        endif;
        add_action( 'suddath_send_reviews', ( $this, 'get_reviews_all_locations' ) );
    endif;
}

public function get_reviews_all_locations(){
    $locations   = $this->get_review_locations_list();
    
    foreach( $locations as $location ):
        $assigned_to = get_field( 'location_id', $location );
        
        if( $assigned_to ):
            $args             = $this->get_review_args( $assigned_to );
            $location_reviews = $this->get_location_reviews( $args );
        endif;
        
        $this->create_html_template( $location, $location_reviews );
        $pdf_location = plugin_dir_path( __DIR__ ) . 'admin/partials/pdf.html';
        $this->generate_market_pdf( $pdf_location, $location->slug, $location->name );
    endforeach;
}