Server : LiteSpeed
System : Linux premium92.web-hosting.com 4.18.0-553.44.1.lve.el8.x86_64 #1 SMP Thu Mar 13 14:29:12 UTC 2025 x86_64
User : rbnsfqys ( 805)
PHP Version : 8.1.33
Disable Function : NONE
Directory :  /home/rbnsfqys/ali.rbn.services/wp-content/plugins/Repairplugin-pro/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]


Current File : /home/rbnsfqys/ali.rbn.services/wp-content/plugins/Repairplugin-pro/dynamic_pricing.php
<?php

// Exit if accessed directly

defined( 'ABSPATH' ) || exit;

if(!defined('RP_DP_PLUGIN_FILE')) {

    define( 'RP_DP_PLUGIN_FILE', __FILE__ );

    define( 'RP_DP_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );

    define( 'RP_DP_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );

    define( 'RP_DP_PLUGIN_URL', plugin_dir_url( __FILE__ ) );

    define( 'RP_DP_ASSETS_CACHE_V', WP_REPAIR_ASSETS_CACHE_V );

    define( 'RP_DP_BASE_SERVER_URL', WP_REPAIR_BASE_SERVER_URL );

    define( 'RP_DP_REPEAT_CRON_AFTER_X_SECONDS', ( 15 * 60 ) ); // 15 minutes

}

function rp_round_up_nearest_ten($price, $endWith = 95) {
    // if price is negative, return price
    if($price < 0) {
        return $price;
    }
    // if $endWith false, return ceil($price)
    if( $endWith === false || $endWith === 'ceil') {
        return ceil($price);
    }
    $endWith = (int) $endWith;
    // get the integer part of the price
    $integer = floor($price);
    // roundup integer to nearest 10
    $integer = ceil($integer / 10) * 10;
    // if decimal is 0.00, return integer
    $integer = $integer - (1 - ( $endWith / 100 ));
    if($integer < $price) {
        $integer = $integer + 10;
    }
    // format the output
    $output = number_format($integer, 2, '.', '');
    // if ends with .00, return integer
    if($output == floor($output)) {
        $output = floor($output);
    }
    return $output;

}

function rp_round_up_nearest_five($price, $endWith = 95) {
    // if price is negative, return price
    if($price < 0) {
        return $price;
    }
    // if $endWith false, return ceil($price)
    if( $endWith === false || $endWith === 'ceil') {
        return ceil($price);
    }
    $endWith = (int) $endWith;
    // get the integer part of the price
    $integer = floor($price);
    // roundup integer to nearest 5
    $integer = ceil($integer / 5) * 5;
    // if decimal is 0.00, return integer
    $integer = $integer - (1 - ( $endWith / 100 ));
    if($integer < $price) {
        $integer = $integer + 5;
    }
    // format the output
    $output = number_format($integer, 2, '.', '');
    // if ends with .00, return integer
    if($output == floor($output)) {
        $output = floor($output);
    }
    return $output;

}

function rp_dynamic_pricing_require_message() {
    // dynamic pricing addon require Repairplugin to be installed and activated
    $mesesage = 'Dynamic Pricing addon require Repairplugin to be installed and activated';
    echo '<div class="notice notice-error is-dismissible"><p>' . $mesesage . '</p></div>';

    return;
}

function rp_dynamic_pricing_addon_init() {

    if( !defined('WP_REPAIR_PLUGIN_FILE') ) {
        // Add admin notice
        rp_add_action('admin_notices', 'rp_dynamic_pricing_require_message');
        return;
    }

    do_action('rp_dynamic_pricing_initialize');

}

rp_add_action('init', 'rp_dynamic_pricing_addon_init', 1000);


function rp_add_dynamic_pricing_tab_in_sidebar( $sidebar_items ) {
    // Example:
    // 'dynamic_pricing' => array(
    //     'title' => 'Dynamic Pricing',
    //     'icon' => 'fas fa-dollar-sign',
    //     'link' => createTheLink('dynamic_pricing'),
    //     'render_condition' => true,
    //     'position' => 65
    // )

    $sidebar_items['dynamic_pricing'] = array(
        'title' => 'Dynamic Pricing',
        'icon' => 'fas fa-dollar-sign',
        'link' => createTheLink('dynamic_pricing'),
        'render_condition' => rp_current_user_has_full_access_cached(),
        'position' => 65
    );

    return $sidebar_items;

}

add_filter('rp_admin_settings_sidebar_items', 'rp_add_dynamic_pricing_tab_in_sidebar');


function rp_dynamic_pricing_tab_actual_html() {
    
    $fileName = RP_DP_PLUGIN_PATH . 'html/dynamic_pricing_settings_tab.php';

    $fileName = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $fileName);

    require_once $fileName;

}

function rp_handle_dynamic_pricing_page_html( $return = FALSE, $section = '' ) {

    if( $section == 'dynamic_pricing' ) {
        // return the html for the dynamic pricing page
        rp_dynamic_pricing_tab_actual_html();

        return TRUE;

    }

    return $return;

}

add_filter('rp_returning_admin_setting_page_html', 'rp_handle_dynamic_pricing_page_html', 10, 2);

function rp_track_last_failed_download_suppliers() {

    rp_update_option('rp_dp_last_failed_download_suppliers', time() );

}

function rp_can_create_download_suppliers_request( $delayAfterFailInSeconds = 60 ) {

    $last_failed_download = rp_get_option('rp_dp_last_failed_download_suppliers', 0);

    if( empty( $last_failed_download ) || !is_numeric( $last_failed_download ) ) {

        // If last failed download is not set or not a number, return TRUE
        return TRUE;

    }

    // Please make sure at least $delayAfterFailInSeconds seconds is passed after last failed download
    $current_time = time();

    $time_difference = $current_time - $last_failed_download;

    if( $time_difference >= $delayAfterFailInSeconds ) {

        // If more than $delayAfterFailInSeconds seconds has passed since last failed download, return TRUE
        return TRUE;

    }

    // If less than $delayAfterFailInSeconds seconds has passed, return FALSE
    return FALSE;

}

function rp_dp_download_suppliers() {

    static $suppliers = NULL;

    if( $suppliers !== NULL ) {

        return $suppliers;

    }

    if( !rp_can_create_download_suppliers_request( 300 ) ) {

        // Avoid creating more requests for next 5 minutes

        $suppliers = array();

        return $suppliers;

    }

    $dp_license = FALSE;

    if( rp_is_enabled_dynamic_pricing_fast_check() ) {

        $dp_license = TRUE;

    }

    $plugin_currency = rp_get_selected_currency_letters();

    global $rpQuery;

	require_once RP_DP_PLUGIN_PATH . 'classes/Base_API.php';

	$base_api = new RepairPluginPro\Base_API($rpQuery);

    $payload = apply_filters('rp_download_dp_suppliers_payload', array('dp_version' => 'v2', 'plugin_currency' => $plugin_currency));
    
	$response = $base_api->fetch('Endpoints/fetch_suppliers', $payload, FALSE, $dp_license);
	
	$suppliers = array();
	
	if( isset( $response ) && is_object( $response ) && property_exists( $response, 'suppliers' ) ) {
	
		$suppliers = $response->suppliers;
	
	}

    if( empty( $suppliers ) ) {

        // Track failed request!
        rp_track_last_failed_download_suppliers();

    }

    return $suppliers;

}

function rp_dp_get_valid_attributes_of_supplier( $supplier_id = 0 ) {

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers(1800);

    if( !empty( $suppliers ) ) {

        foreach($suppliers as $supplier) {

            if( $supplier->id == $supplier_id ) {

                if( property_exists( $supplier, 'valid_attributes' ) ) {

                    if( !empty( $supplier->valid_attributes ) ) {

                        try {

                            return json_decode( $supplier->valid_attributes, TRUE );
    
                        } catch( \Exception|\Error|\Throwable|\ErrorException $e ) {
    
                            return array();
    
                        }

                    } else {

                        return array();

                    }

                }

            }

        }

    }

    return array();

}

function _rp_dp_handle_currency_conversion( $filtered = array() ) {

    if( !empty( $filtered ) ) {

        $final_filter = array();

        foreach( $filtered as $key => $row ) {

            $part_price = rp_supplier_currency_to_plugin_currency( $row->supplier_id, $row->price );

            $row->original_part_price = $row->price;

            $row->converted_part_price = $row->price;

            $row->converted_heading = '';

            $row->plugin_currency = rp_get_selected_currency_letters();

            $row->supplier_currency = rp_get_supplier_currency_by_supplier_id( $row->supplier_id );

            // It means the currency isn't same
            // + No conversion rate found
            // so we cannot convert the price
            // In this case, better to skip that SKU
            if( $part_price !== FALSE ) {

                if( $part_price !== $row->price ) {

                    list( $rate, $fromText, $toText ) = rp_get_currency_conversion_badge_info( $row->supplier_currency );

                    if( $rate !== FALSE ) {

                        $row->converted_heading = $fromText . ' => ' . $toText;

                    }

                }

                $row->price = $part_price;

                $row->converted_part_price = $part_price;

                $final_filter[ $key ] = $row;

            }

        }

        $filtered = $final_filter;

    }

    return $filtered;
    
}

add_filter('rp_dp_get_skus_filtered', '_rp_dp_handle_currency_conversion', 10, 1);

function rp_detect_outdated_names_after_v2_update() {

    global $rpQuery;

    $rs_dr_translations = $rpQuery->prefix . 'rs_dr_translations';

    $need_to_fix_attributes = rp_get_need_to_fix_attributes_after_v2_update();

    if( empty( $need_to_fix_attributes ) ) {

        return false;

    }

    $compatible = $need_to_fix_attributes['compatible'] ?? array();

    $compatible_budget = $need_to_fix_attributes['compatible_budget'] ?? array();

    if( !empty( $compatible ) && !empty( $compatible_old_names = ( $compatible['old_names'] ?? array() ) ) && !empty( $compatible_da_uq_ids = ( $compatible['da_uq_ids'] ?? array() ) ) ) {

        // check if rs_dr_translations contain the old names where uq_ids are in $compatible_da_uq_ids

        $imploded_uqids = "'" . implode("','", $compatible_da_uq_ids) . "'";

        $imploded_old_names = "'" . implode("','", $compatible_old_names) . "'";

        $sql = "SELECT * FROM $rs_dr_translations
        WHERE `key` = 'attr_name' AND ( `language` = 'English (United States)' OR `language` = 'Dutch (Netherlands)' )
        AND `value` IN ($imploded_old_names)
        AND `uq_id` IN ($imploded_uqids) LIMIT 1";

        $row = $rpQuery->get_row($sql);

        if( !empty( $row ) ) {

            return true;

        }

    }

    if( !empty( $compatible_budget ) && !empty( $compatible_budget_old_names = ( $compatible_budget['old_names'] ?? array() ) ) && !empty( $compatible_budget_da_uq_ids = ( $compatible_budget['da_uq_ids'] ?? array() ) ) ) {

        // check if rs_dr_translations contain the old names where uq_ids are in $compatible_budget_da_uq_ids

        $imploded_uqids = "'" . implode("','", $compatible_budget_da_uq_ids) . "'";

        $imploded_old_names = "'" . implode("','", $compatible_budget_old_names) . "'";

        $sql = "SELECT * FROM $rs_dr_translations
        WHERE `key` = 'attr_name' AND ( `language` = 'English (United States)' OR `language` = 'Dutch (Netherlands)' )
        AND `value` IN ($imploded_old_names)
        AND `uq_id` IN ($imploded_uqids) LIMIT 1";

        $row = $rpQuery->get_row($sql);

        if( !empty( $row ) ) {

            return true;

        }

    }

    return false;

}

function rp_dp_get_attribute_mapping_update_alert_data() {
    
    $enabled_suppliers = rp_get_enabled_suppliers();

    if( empty( $enabled_suppliers ) ) {

        return FALSE;

    }

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers(1800);

    $affected_suppliers = array();

    $new_attributes = array();
    
    $removed_attributes = array();

    if( !empty( $suppliers ) ) {

        foreach( $suppliers as $supplier ) {

            $supplier_id = $supplier->id;

            $supplierName = $supplier->name ?? 'Unknown';

            $new_attributes[ $supplierName ] = array();

            $removed_attributes[ $supplierName ] = array();

            list($dp_v2_repair_uqids_by_category, $dp_v2_attr_uqids_by_category, $rp_base_attributes_for_linking, $valid_attributes, $linked_attributes) = rp_get_data_for_dynamic_pricing_v2_attributes_linking( $supplier_id );

            $originally_linked_attributes = rp_dp_get_linked_attributes_of_supplier( $supplier_id, FALSE );

            $last_valid_attributes = rp_get_option('rp_dp_v2_last_valid_attributes_' . $supplier_id, FALSE);

            $last_originally_linked_attributes = rp_get_option('rp_dp_v2_last_originally_linked_attributes_' . $supplier_id, FALSE);

            $custom_configuration = rp_get_option('rp_dp_v2_linked_attributes_' . $supplier_id, FALSE);

            if( $last_valid_attributes === FALSE || $last_originally_linked_attributes === FALSE || empty( $custom_configuration ) || !in_array( $supplier_id, $enabled_suppliers ) ) {

                // If custom configurations aren't done or last info is just saved, skip!

                rp_update_option('rp_dp_v2_last_valid_attributes_' . $supplier_id, $valid_attributes );

                rp_update_option('rp_dp_v2_last_originally_linked_attributes_' . $supplier_id, $originally_linked_attributes );

                continue;

            }

            $valid_attributes_matches = json_encode( $valid_attributes ) == json_encode( $last_valid_attributes );

            $originally_linked_attributes_matches = json_encode( $originally_linked_attributes ) == json_encode( $last_originally_linked_attributes );

            if( !$valid_attributes_matches || !$originally_linked_attributes_matches ) {

                if( !in_array( $supplierName, $affected_suppliers ) ) {

                    $affected_suppliers[] = $supplierName;

                }

                if( !$valid_attributes_matches ) {

                    // Find new attributes
                    foreach( $valid_attributes as $key => $value ) {

                        if( !in_array( $value, $last_valid_attributes ) ) {

                            if( !in_array( $value, $new_attributes[ $supplierName ] ) ) {

                                $new_attributes[ $supplierName ][] = $value;

                            }

                        }

                    }

                    // Find removed attributes
                    foreach( $last_valid_attributes as $key => $value ) {

                        if( !in_array( $value, $valid_attributes ) ) {

                            if( !in_array( $value, $removed_attributes[ $supplierName ] ) ) {

                                $removed_attributes[ $supplierName ][] = $value;

                            }

                        }

                    }

                }

            }

        }

    }

    if( empty( $affected_suppliers ) ) {

        return FALSE;

    }

    // loop through new attributes and remove those which have empty array
    foreach( $new_attributes as $supplierName => $attributes ) {

        if( empty( $attributes ) ) {

            unset( $new_attributes[ $supplierName ] );

        }

    }

    // loop through removed attributes and remove those which have empty array
    foreach( $removed_attributes as $supplierName => $attributes ) {

        if( empty( $attributes ) ) {

            unset( $removed_attributes[ $supplierName ] );

        }

    }

    $data = array(
        'affected_suppliers' => $affected_suppliers,
        'new_attributes' => $new_attributes,
        'removed_attributes' => $removed_attributes
    );

    return $data;

}

function rp_dp_get_unused_attribute_alert_data() {
    
    $enabled_suppliers = rp_get_enabled_suppliers();

    if( empty( $enabled_suppliers ) ) {

        return FALSE;

    }

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers(1800);

    $unused = array();

    if( !empty( $suppliers ) ) {

        foreach( $suppliers as $supplier ) {

            $supplier_id = $supplier->id;

            $supplierName = $supplier->name ?? 'Unknown';

            list($dp_v2_repair_uqids_by_category, $dp_v2_attr_uqids_by_category, $rp_base_attributes_for_linking, $valid_attributes, $linked_attributes) = rp_get_data_for_dynamic_pricing_v2_attributes_linking( $supplier_id );

            // Here, try to find unmatched attributes
            $screen_available_qualities = rp_dp_show_available_attributes_for_linking( $supplier_id, 'screen' );

            $battery_available_qualities = rp_dp_show_available_attributes_for_linking( $supplier_id, 'battery' );

            $other_available_qualities = rp_dp_show_available_attributes_for_linking( $supplier_id, 'other' );

            $all_available_qualities = array( 
                'screen' => $screen_available_qualities,
                'battery' => $battery_available_qualities,
                'other' => $other_available_qualities
            );

            $last_all_available_qualities = rp_get_option('rp_dp_v2_last_all_available_qualities_' . $supplier_id, FALSE);

            if( $last_all_available_qualities === FALSE || !in_array( $supplier_id, $enabled_suppliers ) ) {

                // If last info is just saved, skip!
                rp_update_option('rp_dp_v2_last_all_available_qualities_' . $supplier_id, $all_available_qualities );

                continue;

            }

            $matches = json_encode( $all_available_qualities ) == json_encode( $last_all_available_qualities );

            if( !$matches ) {

                $screen_used_attributes = $linked_attributes['screen' ] ?? array();

                $battery_used_attributes = $linked_attributes['battery' ] ?? array();

                $other_used_attributes = $linked_attributes['other' ] ?? array();

                $filtered_unused = array();

                foreach( $screen_available_qualities as $available_quality ) {

                    $found = FALSE;

                    foreach( $screen_used_attributes as $attrName => $qualities ) {

                        foreach( $qualities as $used_quality ) {

                            if( $available_quality == $used_quality ) {

                                $found = TRUE;

                            }

                        }

                    }

                    if( $found == FALSE ) {

                        if( !isset( $filtered_unused['screen'] ) ) {

                            $filtered_unused['screen'] = array();

                        }

                        $filtered_unused['screen'][] = $available_quality;

                    }

                }

                foreach( $battery_available_qualities as $available_quality ) {

                    $found = FALSE;

                    foreach( $battery_used_attributes as $attrName => $qualities ) {

                        foreach( $qualities as $used_quality ) {

                            if( $available_quality == $used_quality ) {

                                $found = TRUE;

                            }

                        }

                    }

                    if( $found == FALSE ) {

                        if( !isset( $filtered_unused['battery'] ) ) {

                            $filtered_unused['battery'] = array();

                        }

                        $filtered_unused['battery'][] = $available_quality;

                    }

                }

                foreach( $other_available_qualities as $available_quality ) {

                    $found = FALSE;

                    foreach( $other_used_attributes as $attrName => $qualities ) {

                        foreach( $qualities as $used_quality ) {

                            if( $available_quality == $used_quality ) {

                                $found = TRUE;

                            }

                        }

                    }

                    if( $found == FALSE ) {

                        if( !isset( $filtered_unused['other'] ) ) {

                            $filtered_unused['other'] = array();

                        }

                        $filtered_unused['other'][] = $available_quality;

                    }

                }

                if( !empty( $filtered_unused ) ) {

                    $unused[ $supplierName ] = $filtered_unused;
                    
                }

            }

        }

    }

    if( empty( $unused ) ) {

        return FALSE;

    }

    return $unused;

}

function rp_track_dynamic_pricing_status_change() {

    $status = rp_is_enabled_dynamic_pricing() === TRUE ? '1' : '0';

    $last_status = rp_get_option('rp_dp_last_status', '');

    if( $last_status !== $status ) {

        // Update the status
        rp_update_option('rp_dp_last_status', $status);

        rp_update_option('rp_dp_last_status_change', time());

    }
    
}

rp_add_action('init', 'rp_track_dynamic_pricing_status_change', 9999);

function rp_need_to_check_sync_process() {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return FALSE;

    }

    $last_status_change = rp_get_option('rp_dp_last_status_change', 0);

    // Please make sure at least 24 hour is passed after last status change
    $current_time = time();

    $time_difference = $current_time - $last_status_change;

    if( $time_difference >= ( 60 * 60 * 23.5 ) ) { // 24 hour in seconds

        // If more than 24 hour has passed since last status change, return TRUE
        return TRUE;

    }

    // If less than 24 hour has passed, return FALSE
    return FALSE;
    
}

function _rp_dp_need_to_show_sync_stop_notice( $timeLimitInHours = 2 ) {

    if( rp_need_to_check_sync_process() === FALSE ) {

        return FALSE;

    }

    $last_outputs_update = rp_get_option('rp_dp_last_outputs_update', 0);

    if( empty( $last_outputs_update ) ) {

        // It seems cron process hasn't run even a single time!
        return TRUE;

    }

    // if it's more than 2 hour since last output update, we need to show the notice
    $current_time = time();

    $time_difference = $current_time - $last_outputs_update;

    if( $time_difference >= ( 60 * 60 * $timeLimitInHours ) ) { // 2 hour in seconds

        // If more than 2 hour has passed since last output update, return TRUE
        return TRUE;

    }

    // If less than 2 hour has passed, return FALSE
    return FALSE;

}

function rp_dp_process_tried_running_recently_through_wp( $timeLimitInHours = 2 ) {

    $last_time = rp_get_option('rp_last_dynamic_pricing_wp_cron_run', 0);

    if( empty( $last_time ) || !is_numeric( $last_time ) ) {

        // If last time is not set or not a number, return FALSE
        return FALSE;

    }

    // Please make sure last request created is within the last 2 hour
    $current_time = time();

    $time_difference = $current_time - $last_time;

    if( $time_difference <= ( 60 * 60 * $timeLimitInHours ) ) { // 2 hour in seconds

        // If less than 2 hour has passed since last request created, return TRUE
        return TRUE;

    }

    // If more than 2 hour has passed, return FALSE
    return FALSE;

}

function rp_dp_need_to_show_sync_stop_notice() {

    if( rp_get_option('rp_which_way_for_processing_datafeed', 'wordpress') == 'wordpress' ) {

        // If it's wordpress, the time is 48 hours because
        // in this scenario, cron process will only run
        // when a website is visited
        return _rp_dp_need_to_show_sync_stop_notice( 47.5 ); // 48 hours

    } else {

        // If it's cron job, the time is 24 hours
        return _rp_dp_need_to_show_sync_stop_notice( 23.5 ); // 24 hours

    }

}

function rp_dp_track_sync_stop_notice_status() {

    $status = rp_dp_need_to_show_sync_stop_notice() === TRUE ? '1' : '0';

    $last_status = rp_get_option('rp_dp_sync_stop_notice_status', '');

    if( $last_status !== $status ) {

        // Update the status
        rp_update_option('rp_dp_sync_stop_notice_status', $status);

        rp_update_option('rp_dp_sync_stop_notice_last_checked', time());

    }

}

rp_add_action('init', 'rp_dp_track_sync_stop_notice_status', 9999);

function rp_dp_really_need_to_show_sync_stop_notice() {

    $status = rp_dp_need_to_show_sync_stop_notice() === TRUE ? '1' : '0';

    $last_status = rp_get_option('rp_dp_sync_stop_notice_status', '');

    $last_status_checked = rp_get_option('rp_dp_sync_stop_notice_last_checked', 0);

    $current_time = time();

    $time_difference = $current_time - $last_status_checked;

    // if status is 1 and is unchanged for more than 30 minutes

    if( $status == '1' && $last_status == '1' && $time_difference >= ( 60 * 30 ) ) {

        // It means we need to show the notice
        return TRUE;

    }

    return FALSE;

}

function rp_prettify_rp_error_email($title = '', $message = '') {
	ob_start();
	?>
    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title><?php echo $title; ?></title>
            <style>
                body {
                    margin: 0;
                    padding: 0;
                    background-color: #f4f4f4;
                    font-family: Arial, sans-serif;
                }
                .bg-container {
                    background-color: #f4f4f4;
                    font-family: Arial, sans-serif;
                    display: block;
                    padding: 40px 0px;
                }
                .email-container {
                    max-width: 600px;
                    margin: 0px auto 0px auto;
                    background: #ffffff;
                    padding: 40px 30px;
                    border-radius: 8px;
                    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
                    text-align: center;
                    box-sizing: border-box;
                }
                h1, h2, h3, p {
                    margin: 0 0 15px 0;
                    color: #333333;
                }
                .activity-table {
                    width: 100%;
                    margin: 30px 0;
                    text-align: left;
                }
                .activity-table td {
                    padding: 8px 0;
                    line-height: 1.5em;
                    vertical-align: top;
                    font-size: 16px;
                    color: #555555;
                }
                .activity-table td:first-child {
                    width: 30px;
                    font-size: 20px;
                }
                .footer {
                    margin-top: 20px;
                    font-size: 14px;
                    color: #999999;
                    text-align: center;
                }
                .small-text-wrapper {
                    max-width: 600px;
                    margin: 10px auto 0px;
                    padding: 0 30px;
                    text-align: center;
                    font-size: 12px;
                    color: #cccccc;
                    font-family: Arial, sans-serif;
                    box-sizing: border-box;
                }
                .small-text-wrapper a {
                    color: inherit;
                    text-decoration: underline;
                }
                .logo {
                    max-width: 150px;
                    margin-bottom: 40px;
                }
                .intro-text {
                    margin-bottom: 30px;
                    text-align: left;
                    font-size: 18px;
                    font-weight: bold;
                }

                .intro-paragraph {
                    text-align: left;
                    font-size: 15px;
                    font-weight: normal;
                    line-height: 1.5em;
                }

                ul {
                    list-style: disc;
                    padding-left: 20px;
                }

                ul li {
                    list-style: disc;
                    margin-bottom: 5px;
                }

                /* Responsive verbeteringen */
                @media only screen and (max-width: 480px) {
                    .bg-container {
                        padding: 20px 0px;
                    }
                    .email-container {
                        margin: 0px 10px 0 10px;
                        padding: 30px 20px;
                    }
                    .small-text-wrapper {
                        padding: 0 20px;
                        font-size: 11px;
                    }
                    .intro-text {
                        font-size: 16px;
                    }
                    .activity-table td {
                        font-size: 15px;
                    }
                    .footer {
                        font-size: 13px;
                    }
                }
            </style>
        </head>
        <body>
            <div class="bg-container">
                <?php echo $message; ?>
            </div>
        </body>
    </html>
	<?php
	$content = ob_get_contents();
	ob_end_clean();
	return $content;
}

function rp_prepare_rp_error_email($title = '', $message = '') {

    include_once WP_REPAIR_PLUGIN_PATH.'html/order_emails.php';

	if(!class_exists('PHPMailer\PHPMailer\PHPMailer')) {

		require_once wp_repair_load_php_mailer('PHPMailer/PHPMailer.php');

	}

	if(!class_exists('PHPMailer\PHPMailer\SMTP')) {

		require_once wp_repair_load_php_mailer('PHPMailer/SMTP.php');

	}

	if(!class_exists('PHPMailer\PHPMailer\Exception')) {

		require_once wp_repair_load_php_mailer('PHPMailer/Exception.php');

	}

	$message = rp_prettify_rp_error_email($title, $message);

	return $message;
}

function rp_send_rp_error_email( $email = '', $subject = '', $text = '' ) {

    $email = trim($email);

    $message_template = '
    <div class="email-container rp-error-email">
        <img src="https://storage.mlcdn.com/account_image/117223/Z9mXL9VN8FtfMG4UMm9JYWQLOAjDGTAxb1s9S4WD.png" alt="RepairPlugin Logo" class="logo">

        <p class="intro-text">'.$subject.'</p>

        <div class="intro-paragraph">'.$text.'</div>

        <div class="footer">
            Made with 💙 at RepairPlugin.<br>
            Built to help repair shops convert online visitors into customers.
        </div>
    </div>
    ';

    // Re-create the message for admin
    $message = rp_prepare_rp_error_email($subject, $message_template);

    wp_repair_send_mail( $email, $subject, $message );

}

function rp_handle_sending_sync_stop_email() {

    if( rp_dp_really_need_to_show_sync_stop_notice() === FALSE ) {

        return;

    }

    $last_status_checked = rp_get_option('rp_dp_sync_stop_notice_last_checked', 0);

    $last_email_sent_for = rp_get_option('rp_dp_sync_stop_notice_last_email_sent_for', 0);

    if( $last_status_checked == $last_email_sent_for ) {

        // Email was already sent

    } else {

        // Update option
        rp_update_option('rp_dp_need_to_show_sync_issue_message', '1');

        // Update so email is sent only once!
        rp_update_option('rp_dp_sync_stop_notice_last_email_sent_for', $last_status_checked);

        $administrator_email = rp_get_option('admin_email', '');

        if( !empty( $administrator_email ) && filter_var( $administrator_email, FILTER_VALIDATE_EMAIL ) ) {

            // Time to send an email!
            rp_send_rp_error_email(
                $administrator_email,
                '⚠️ Dynamic Pricing: Data Sync Issue',
                rp_dp_sync_stop_detected_notice_message()
            );

        }

    }

}

rp_add_action('init', 'rp_handle_sending_sync_stop_email', 9999);

function rp_dp_sync_stop_detected_notice_message() {

    ob_start();

    ?>
    It looks like the dynamic pricing data isn't syncing properly. Here's how to troubleshoot the issue:
    <br><br>
    <b>1. Check the Error Logs</b>
    <ul class="highlight-troubleshoot-sync-process-list">
        <li>Please check <a href="<?php echo admin_url('admin.php?page=wp_repair_settings&section=dynamic_pricing&tab=error_logs#error-logs-section') ?>">error logs</a> related to Dynamic Pricing</li>
        <li>Please check <a href="<?php echo admin_url('admin.php?page=repair-plugin#error-logs-section'); ?>">error logs</a> related to Repairplugin Pro</li>
    </ul>
    <br>
    <b>2. No Errors Found? This Might Be the Reason:</b>
    <br>
    <?php
    if( rp_get_option('rp_which_way_for_processing_datafeed', 'wordpress') !== 'wordpress' ) {
        ?>
        <span style="display: block; margin-top:10px; max-width:820px;">Your site is set to sync dynamic pricing data using cron job, please make sure that the cron job is really working.</span>
        <br>
        <b>What you can do:</b>
        <ul class="highlight-troubleshoot-sync-process-list">
            <li>Please check the <b>last run time</b> at the bottom of the <a href="<?php echo admin_url('admin.php?page=wp_repair_settings&section=dynamic_pricing#cron-job-section'); ?>">Dynamic Pricing Settings</a> tab.</li>
            <li>If the last run time isn't recent, please ensure the <b>cron job is set up correctly</b></li>
        </ul>
        <?php
    } else {
        ?>
        <span style="display: block; margin-top:10px; max-width:820px;">Your site is set to sync dynamic pricing data using WordPress. The sync may not have run yet because it depends on visitors visiting your website. If there haven't been any visitors in the last 48 hours, that could be the reason you're seeing this notice.</span>
        <br>
        <b>What you can do:</b>
        <ul class="highlight-troubleshoot-sync-process-list">
            <li>Try switching to a real cron job, which is the recommended method. It runs independently and doesn't rely on site visitors.</li>
            <li>If it really is just a matter of no recent visitors, the syncing will run automatically once someone visits, and this notice should go away on its own.</li>
        </ul>
        <?php
    }
    ?>
    <br>
    <b>3. Still Stuck?</b>
    <br>
    <span style="display: block; margin-top:10px;">If the issue continues, feel free to <b>contact our support team</b>. We're here to help!</span>
    <?php

    $content = ob_get_clean();

    return $content;

}

function rp_dp_sync_stop_detected_notice() {

    $full_access = rp_current_user_has_full_access_cached();

    if( $full_access == FALSE ) {

        return;

    }

    if( rp_get_option('rp_dp_need_to_show_sync_issue_message', '0') != '1' ) {

        return;

    }

    if( !rp_is_enabled_dynamic_pricing() ) {

        return FALSE;

    }

    wp_enqueue_style('font-awesome-css', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css');

    ?>
    <style type="text/css">
        .wp-repair-alert.rp-dp-sync-issue.info {
            border-radius: 8px;
            padding: 12px;
            font-size: 13px;
            line-height: 1.4em;
            width: 100%;
            box-sizing: border-box;
            background: #f1f8ff;
            border: 1px solid #cfe2f3;
            color: #205072;

        }
        .rp-dp-sync-issue ul.highlight-troubleshoot-sync-process-list {
            padding-left: 20px;
        }
        .rp-dp-sync-issue ul.highlight-troubleshoot-sync-process-list li {
            list-style: disc;
            font-weight: 400;
        }
        .visit-dynamic-pricing-settings-tab,
        .visit-dynamic-pricing-settings-tab:hover,
        .visit-dynamic-pricing-settings-tab:active,
        .visit-dynamic-pricing-settings-tab:focus,
        .visit-dynamic-pricing-settings-tab:visited {
            background: #1275c2 !important;
            color: #fff !important;
            font-weight: 500 !important;
            border: 1px solid #1275c2 !important;
            border-radius: 5px !important;
            padding: 5px 10px !important;
            cursor: pointer !important;
            font-size: 13px !important;
            text-transform: none;
            letter-spacing: normal;
            outline: none;
            text-decoration: none;
            box-shadow: unset;
            display: table;
            width: auto;
            margin-top: 0px !important;
            text-align: center;
            box-sizing: border-box;
        }
        .dp-sync-process-issue-actions {
            display: flex;
            align-items: center;
            margin-top: 20px;
            gap: 5px;
        }
        a.rp-mark-notice-as-reviewed,
        a.rp-mark-notice-as-reviewed:hover,
        a.rp-mark-notice-as-reviewed:active,
        a.rp-mark-notice-as-reviewed:focus {
            outline: 0;
            box-shadow: unset;
        }
    </style>
    <div style="padding: 0px 0px; margin-bottom: 20px; margin-top:20px;">
        <div class="wp-repair-alert rp-dp-sync-issue info">
            <div class="alert-content">
                <i class="fa fa-info-circle"></i>	
                <strong>Dynamic Pricing: Data Sync Issue</strong>
                <br><br>
                <?php echo rp_dp_sync_stop_detected_notice_message(); ?>
                <br><br>
                <span style="font-style: italic;">(This notice will go away on its own once data syncing process starts working again.)</span>
            </div>
        </div>
    </div>
    <?php

}

rp_add_action('admin_notices', 'rp_dp_sync_stop_detected_notice');

function rp_unused_attributes_detected_notice() {

    $full_access = rp_current_user_has_full_access_cached();

    if( $full_access == FALSE ) {

        return;

    }

    $data = rp_dp_get_unused_attribute_alert_data();

    if( empty( $data ) ) {

        return;

    }

    wp_enqueue_style('font-awesome-css', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css');

    ?>
    <style type="text/css">
        .wp-repair-alert.dp-attribute-mapping-issue-alert.info {
            border-radius: 8px;
            padding: 12px;
            font-size: 13px;
            line-height: 1.4em;
            width: 100%;
            box-sizing: border-box;
            background: #f1f8ff;
            border: 1px solid #cfe2f3;
            color: #205072;
        }
        .dp-attribute-mapping-issue-alert ul.highlight-attribute-mapping-issue-list {
            padding-left: 20px;
        }
        .dp-attribute-mapping-issue-alert ul.highlight-attribute-mapping-issue-list li {
            list-style: disc;
            font-weight: 600;
        }
        .visit-dynamic-pricing-settings-tab,
        .visit-dynamic-pricing-settings-tab:hover,
        .visit-dynamic-pricing-settings-tab:active,
        .visit-dynamic-pricing-settings-tab:focus,
        .visit-dynamic-pricing-settings-tab:visited {
            background: #1275c2 !important;
            color: #fff !important;
            font-weight: 500 !important;
            border: 1px solid #1275c2 !important;
            border-radius: 5px !important;
            padding: 5px 10px !important;
            cursor: pointer !important;
            font-size: 13px !important;
            text-transform: none;
            letter-spacing: normal;
            outline: none;
            text-decoration: none;
            box-shadow: unset;
            display: table;
            width: auto;
            margin-top: 0px !important;
            text-align: center;
            box-sizing: border-box;
        }
        .attribute-mapping-issue-actions {
            display: flex;
            align-items: center;
            margin-top: 20px;
            gap: 5px;
        }
        a.rp-mark-notice-as-reviewed,
        a.rp-mark-notice-as-reviewed:hover,
        a.rp-mark-notice-as-reviewed:active,
        a.rp-mark-notice-as-reviewed:focus {
            outline: 0;
            box-shadow: unset;
        }
    </style>
    <div style="padding: 0px 0px; margin-bottom: 20px; margin-top:20px;">
        <div class="wp-repair-alert dp-attribute-mapping-issue-alert info">
            <div class="alert-content">
                <i class="fa fa-info-circle"></i>	
                <strong>Dynamic Pricing: Unused Attributes Detected</strong>
                <br><br>
                We have detected some unused attributes for the following suppliers:
                <ul class="highlight-attribute-mapping-issue-list">
                    <?php
                    foreach( $data as $supplierName => $unused_attributes ) {
                        if( !empty( $supplierName ) ) {

                            echo '<li>' . $supplierName . '</li>';

                        }
                    }
                    ?>
                </ul>
                To avoid missing any crucial connections, please review your attribute mapping by clicking on <b>Manage Attributes</b> button available on <b>Dynamic Pricing Settings</b> tab.
                <div class="attribute-mapping-issue-actions">
                    <a href="admin.php?page=wp_repair_settings&section=dynamic_pricing" class="visit-dynamic-pricing-settings-tab">
                        Go to Dynamic Pricing Settings
                    </a>
                    <a href="javascript:void(0)" class="rp-mark-notice-as-reviewed rp_unused_attributes_detected_notice">Mark as reviewed</a>
                </div>
            </div>
        </div>
    </div>
    <script type="text/javascript" style="display: none;">
        jQuery(document).on('click', '.rp-mark-notice-as-reviewed.rp_unused_attributes_detected_notice', function(){
                
            if( jQuery(this).hasClass('processing') ) {

                return;

            }

            jQuery(this).addClass('processing').text('Marking as reviewed...');

            jQuery.ajax({
                type: 'POST',
                url: "<?php echo rp_get_admin_ajax_url_for_back_end(); ?>",
                data: {
                    action: 'rp_dp_mark_unused_attributes_as_reviewed',
                    confirm: 1
                },
                success: function(response) {
                    try {
                        response = JSON.parse(response);
                    } catch (e) {
                        response = { status: false, message: 'Invalid response from server.' };
                    }
                    if( response && response.status && (response.status === true || response.status === 1 || response.status === 'true') ) {

                        jQuery('.rp_unused_attributes_detected_notice').closest('.wp-repair-alert').parent().remove();

                    } else {

                        jQuery('.rp_unused_attributes_detected_notice').removeClass('processing').text('Mark as reviewed');

                        let errorMessage = response.message || 'An error occurred. Please try again.';

                        alert(errorMessage);

                    }
                },
                error: function() {
                    alert('An error occurred. Please try again.');
                }
            });

        });
    </script>
    <?php

}

function rp_dp_mark_unused_attributes_as_reviewed() {

    if( ! ($_REQUEST['confirm'] ?? false) ) {

        echo json_encode(array('status' => false, 'message' => 'Invalid request.'));

        exit();

    }

    if( rp_current_user_has_full_access_cached() == false ) {

        echo json_encode(array('status' => false, 'message' => 'You do not have permission to perform this action.'));

        exit();

    }

    // Mark as reviewed
    // Remove all option names that start with rp_dp_v2_last_all_available_qualities_
    // and recall the function
    global $rpQuery;

    $options = $rpQuery->prefix . 'options';

    $sql = "DELETE FROM $options WHERE `option_name` LIKE 'rp_dp_v2_last_all_available_qualities_%'";

    $rpQuery->query($sql);

    // now recall the function
    rp_dp_get_unused_attribute_alert_data();

    echo json_encode(array('status' => true, 'message' => 'All looks good'));

    exit();

}

rp_ajax_for_admin('rp_dp_mark_unused_attributes_as_reviewed', 'rp_dp_mark_unused_attributes_as_reviewed');

function rp_dp_mark_attributes_mapping_as_reviewed() {

    if( ! ($_REQUEST['confirm'] ?? false) ) {

        echo json_encode(array('status' => false, 'message' => 'Invalid request.'));

        exit();

    }

    if( rp_current_user_has_full_access_cached() == false ) {

        echo json_encode(array('status' => false, 'message' => 'You do not have permission to perform this action.'));

        exit();

    }

    // Mark as reviewed
    // Remove all option names that start with rp_dp_v2_last_valid_attributes_ || rp_dp_v2_last_originally_linked_attributes_
    // and recall the function
    global $rpQuery;

    $options = $rpQuery->prefix . 'options';

    $sql = "DELETE FROM $options WHERE `option_name` LIKE 'rp_dp_v2_last_valid_attributes_%'";

    $rpQuery->query($sql);

    $sql = "DELETE FROM $options WHERE `option_name` LIKE 'rp_dp_v2_last_originally_linked_attributes_%'";

    $rpQuery->query($sql);

    // now recall the function
    rp_dp_get_attribute_mapping_update_alert_data();

    echo json_encode(array('status' => true, 'message' => 'All looks good'));

    exit();

}

rp_ajax_for_admin('rp_dp_mark_attributes_mapping_as_reviewed', 'rp_dp_mark_attributes_mapping_as_reviewed');

function rp_attributes_mapping_updated_notice() {

    $full_access = rp_current_user_has_full_access_cached();

    if( $full_access == FALSE ) {

        return;

    }

    $data = rp_dp_get_attribute_mapping_update_alert_data();

    if( empty( $data ) ) {

        return;

    }

    $affected_suppliers = $data['affected_suppliers'] ?? array();

    $new_attributes = $data['new_attributes'] ?? array();

    $removed_attributes = $data['removed_attributes'] ?? array();

    wp_enqueue_style('font-awesome-css', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css');

    ?>
    <style type="text/css">
        .wp-repair-alert.dp-attribute-mapping-issue-alert.info {
            border-radius: 8px;
            padding: 12px;
            font-size: 13px;
            line-height: 1.4em;
            width: 100%;
            box-sizing: border-box;
            background: #f1f8ff;
            border: 1px solid #cfe2f3;
            color: #205072;
        }
        .dp-attribute-mapping-issue-alert ul.highlight-attribute-mapping-issue-list {
            padding-left: 20px;
        }
        .dp-attribute-mapping-issue-alert ul.highlight-attribute-mapping-issue-list li {
            list-style: disc;
            font-weight: 600;
        }
        .visit-dynamic-pricing-settings-tab,
        .visit-dynamic-pricing-settings-tab:hover,
        .visit-dynamic-pricing-settings-tab:active,
        .visit-dynamic-pricing-settings-tab:focus,
        .visit-dynamic-pricing-settings-tab:visited {
            background: #1275c2 !important;
            color: #fff !important;
            font-weight: 500 !important;
            border: 1px solid #1275c2 !important;
            border-radius: 5px !important;
            padding: 5px 10px !important;
            cursor: pointer !important;
            font-size: 13px !important;
            text-transform: none;
            letter-spacing: normal;
            outline: none;
            text-decoration: none;
            box-shadow: unset;
            display: table;
            width: auto;
            margin-top: 0px !important;
            text-align: center;
            box-sizing: border-box;
        }
        .attribute-mapping-issue-actions {
            display: flex;
            align-items: center;
            margin-top: 20px;
            gap: 5px;
        }
        a.rp-mark-notice-as-reviewed,
        a.rp-mark-notice-as-reviewed:hover,
        a.rp-mark-notice-as-reviewed:active,
        a.rp-mark-notice-as-reviewed:focus {
            outline: 0;
            box-shadow: unset;
        }
    </style>
    <div style="padding: 0px 0px; margin-bottom: 20px; margin-top:20px;">
        <div class="wp-repair-alert dp-attribute-mapping-issue-alert info">
            <div class="alert-content">
                <i class="fa fa-info-circle"></i>	
                <strong>Dynamic Pricing: Attribute Mapping Is Updated In RepairPlugin Pro Server</strong>
                <br><br>
                The following suppliers are affected by attribute mapping updates received from the RepairPlugin Pro server:
                <ul class="highlight-attribute-mapping-issue-list">
                    <?php
                    foreach( $affected_suppliers as $supplierName ) {
                        if( !empty( $supplierName ) ) {

                            echo '<li>' . $supplierName . '</li>';

                        }
                    }
                    ?>
                </ul>
                <?php
                if( !empty( $new_attributes ) ) {
                    ?>
                    <br>
                    New attributes since you have last reviewed:
                    <ul class="highlight-attribute-mapping-issue-list">
                        <?php
                        foreach( $new_attributes as $supplierName => $attributes ) {
                            if( !empty( $supplierName ) && !empty( $attributes ) ) {

                                // sort attributes by name
                                sort( $attributes );

                                echo '<li>' . $supplierName . ' → ' . implode(', ', $attributes) . '</li>';

                            }
                        }
                        ?>
                    </ul>
                    <?php
                }

                if( !empty( $removed_attributes ) ) {
                    ?>
                    <br>
                    Removed attributes since you have last reviewed:
                    <ul class="highlight-attribute-mapping-issue-list">
                        <?php
                        foreach( $removed_attributes as $supplierName => $attributes ) {
                            if( !empty( $supplierName ) && !empty( $attributes ) ) {

                                // sort attributes by name
                                sort( $attributes );

                                echo '<li>' . $supplierName . ' → ' . implode(', ', $attributes) . '</li>';

                            }
                        }
                        ?>
                    </ul>
                    <?php
                }

                if( !empty( $new_attributes ) || !empty( $removed_attributes ) ) {
                    ?>
                    <br>
                    <?php
                }
                ?>
                To avoid missing any crucial connections, please review your attribute mapping by clicking on <b>Manage Attributes</b> button available on <b>Dynamic Pricing Settings</b> tab.
                <div class="attribute-mapping-issue-actions">
                    <a href="admin.php?page=wp_repair_settings&section=dynamic_pricing" class="visit-dynamic-pricing-settings-tab">
                        Go to Dynamic Pricing Settings
                    </a>
                    <a href="javascript:void(0)" class="rp-mark-notice-as-reviewed rp_attributes_mapping_updated_notice">Mark as reviewed</a>
                </div>
            </div>
        </div>
    </div>
    <script type="text/javascript" style="display: none;">
        jQuery(document).on('click', '.rp-mark-notice-as-reviewed.rp_attributes_mapping_updated_notice', function(){
                
            if( jQuery(this).hasClass('processing') ) {

                return;

            }

            jQuery(this).addClass('processing').text('Marking as reviewed...');

            jQuery.ajax({
                type: 'POST',
                url: "<?php echo rp_get_admin_ajax_url_for_back_end(); ?>",
                data: {
                    action: 'rp_dp_mark_attributes_mapping_as_reviewed',
                    confirm: 1
                },
                success: function(response) {
                    try {
                        response = JSON.parse(response);
                    } catch (e) {
                        response = { status: false, message: 'Invalid response from server.' };
                    }
                    if( response && response.status && (response.status === true || response.status === 1 || response.status === 'true') ) {

                        jQuery('.rp_attributes_mapping_updated_notice').closest('.wp-repair-alert').parent().remove();

                    } else {

                        jQuery('.rp_attributes_mapping_updated_notice').removeClass('processing').text('Mark as reviewed');

                        let errorMessage = response.message || 'An error occurred. Please try again.';

                        alert(errorMessage);

                    }
                },
                error: function() {
                    alert('An error occurred. Please try again.');
                }
            });

        });
    </script>
    <?php

}

// add admin notice
rp_add_action( 'admin_notices', 'rp_attributes_mapping_updated_notice' );

// add admin notice
rp_add_action( 'admin_notices', 'rp_unused_attributes_detected_notice' );

function rp_dp_show_available_attributes_for_linking( $supplier_id = 0, $repair_type = '' ) {

    $supplier_id = (int) $supplier_id;
    
    if( empty( $supplier_id ) || empty( $repair_type ) ) {

        return array();

    }

    $repair_type = trim( strtolower( $repair_type ) );

    list($dp_v2_repair_uqids_by_category, $dp_v2_attr_uqids_by_category, $rp_base_attributes_for_linking, $valid_attributes, $linked_attributes) = rp_get_data_for_dynamic_pricing_v2_attributes_linking( $supplier_id );

    $uq_ids = $dp_v2_repair_uqids_by_category[ $repair_type ?? '' ] ?? array();

    if( empty( $uq_ids ) ) {

        return array();

    }

    global $rpQuery;

    $rs_dp_linking = $rpQuery->prefix . 'rs_dp_linking';

    $repair_uqids = "'" . implode("','", $uq_ids) . "'";

    $sql = "SELECT `attribute` FROM $rs_dp_linking WHERE supplier_id = {$supplier_id} AND repair_uqid IN ($repair_uqids) GROUP BY `attribute`";

    $rows = $rpQuery->get_results($sql);

    $available_qualities = array();

    foreach( $rows as $row ) {

        if( !empty( $row->attribute ) ) {

            $available_qualities[] = $row->attribute;

        }

    }

    $available_qualities = array_unique( $available_qualities );

    $available_qualities = array_values( $available_qualities );

    if( !empty( $available_qualities ) ) {

        // trim 
        foreach( $available_qualities as $key => $value ) {

            $available_qualities[ $key ] = trim( $value );

        }

    }

    $filtered = array();

    $valid_attributes = $valid_attributes ?? array();

    if( !empty( $valid_attributes ) ) {

        // trim 
        foreach( $valid_attributes as $key => $value ) {

            $valid_attributes[ $key ] = trim( $value );

        }

    }

    // make sure all available qualities are in valid attributes
    foreach( $available_qualities as $quality ) {

        if( in_array( $quality, $valid_attributes ) ) {

            $filtered[] = $quality;

        }

    }

    return $filtered;

}

function rp_get_need_to_fix_attributes_after_v2_update() {

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers_with_need_to_fix_attributes();

    if( !empty( $suppliers ) ) {

        $need_to_fix_attributes = array();

        foreach($suppliers as $supplier) {

            if( !empty( $supplier ) && property_exists( $supplier, 'need_to_fix_attributes' ) ) {

                return json_decode( json_encode( $supplier->need_to_fix_attributes ), TRUE );

            }

        }

        return $need_to_fix_attributes;

    }

    return array();

}

function rp_get_cached_or_download_suppliers_with_need_to_fix_attributes() {

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers(1800);

    if( !empty( $suppliers ) ) {
        
        $need_to_refetch = true;

        foreach($suppliers as $supplier) {

            if( !empty( $supplier ) && property_exists( $supplier, 'need_to_fix_attributes' ) ) {

                $need_to_refetch = false;

            }

        }

        if( $need_to_refetch === true ) {

            rp_update_option('rp_dp_last_downloaded_suppliers', FALSE);
    
            list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers(1800);
    
        }

    }

    return array($suppliers, $last_synced);

}

function rp_rename_old_attributes_after_dp_v2_update() {

    if( ( $_REQUEST['confirm'] ?? 0 ) != '1' ) {

        echo json_encode(array('status' => false, 'rp_validation_error' => 'Invalid Request'));

        exit();

    }

    if( rp_current_user_has_full_access_cached() == false ) {

        echo json_encode(array('status' => false, 'rp_validation_error' => 'You do not have permission to perform this action.'));

        exit();

    }

    if( rp_detect_outdated_names_after_v2_update() == false ) {

        echo json_encode(array('status' => false, 'rp_validation_error' => 'No need to fix any attributes.'));

        exit();

    }

    global $rpQuery;

    $rs_dr_translations = $rpQuery->prefix . 'rs_dr_translations';

    $rs_languages = $rpQuery->prefix . 'rs_languages';

    $languages_rows = $rpQuery->get_results("SELECT * FROM $rs_languages");

    $downloaded_languages_name = array();

    if( !empty( $languages_rows ) ) {

        foreach( $languages_rows as $language_row ) {

            $downloaded_languages_name[] = ($language_row->name ?? '');
        
        }

    }

    $need_to_fix_attributes = rp_get_need_to_fix_attributes_after_v2_update();

    $compatible = $need_to_fix_attributes['compatible'] ?? array();

    $compatible_budget = $need_to_fix_attributes['compatible_budget'] ?? array();

    $rpQuery->query("START TRANSACTION");

    if( !empty( $compatible ) && !empty( $compatible_translations = ( $compatible['translations'] ?? array() ) ) && !empty( $compatible_da_uq_ids = ( $compatible['da_uq_ids'] ?? array() ) ) ) {

        $affected_keys = array('attr_name', 'attr_desc');

        $affected_languages = array();

        foreach( $compatible['translations'] as $translation_row ) {

            if( !in_array( $translation_row['language'], $affected_languages ) ) {

                $affected_languages[] = $translation_row['language'];

            }

        }

        $affected_uqids = $compatible_da_uq_ids;

        // delete all the rows where uq_ids are in $compatible_da_uq_ids
        // and key is in $affected_keys
        // and language is in $affected_languages
        $affected_keys_str = "'" . implode("','", $affected_keys) . "'";

        $affected_languages_str = "'" . implode("','", $affected_languages) . "'";

        $affected_uqids_str = "'" . implode("','", $affected_uqids) . "'";

        $rpQuery->query("DELETE FROM $rs_dr_translations
        WHERE `uq_id` IN ($affected_uqids_str) 
        AND `key` IN ($affected_keys_str) 
        AND `language` IN ($affected_languages_str)");

        // insert the new rows
        foreach( $compatible_translations as $translation_row ) {

            $language = $translation_row['language'] ?? '';

            if( !in_array( $language, $downloaded_languages_name ) ) {

                // Skip if not downloaded language
                continue;

            }

            $key = $translation_row['key'] ?? '';

            $value = $translation_row['value'] ?? '';

            if( !empty( $language ) && !empty( $key ) && !empty( $value ) ) {

                foreach( $compatible_da_uq_ids as $uq_id ) {

                    if( !empty( $uq_id ) && !empty( $language ) && !empty( $key ) && !empty( $value ) ) {

                        $rpQuery->query("INSERT INTO $rs_dr_translations (`uq_id`, `language`, `key`, `value`) VALUES ('".rp_escape_sql($uq_id)."', '".rp_escape_sql($language)."', '".rp_escape_sql($key)."', '".rp_escape_sql($value)."')");
        
                    }

                }

            }

        }

    }

    if( !empty( $compatible_budget ) && !empty( $compatible_budget_translations = ( $compatible_budget['translations'] ?? array() ) ) && !empty( $compatible_budget_da_uq_ids = ( $compatible_budget['da_uq_ids'] ?? array() ) ) ) {

        $affected_keys = array('attr_name', 'attr_desc');

        $affected_languages = array();

        foreach( $compatible_budget['translations'] as $translation_row ) {

            if( !in_array( $translation_row['language'], $affected_languages ) ) {

                $affected_languages[] = $translation_row['language'];

            }

        }

        $affected_uqids = $compatible_budget_da_uq_ids;

        // delete all the rows where uq_ids are in $compatible_budget_da_uq_ids
        // and key is in $affected_keys
        // and language is in $affected_languages
        $affected_keys_str = "'" . implode("','", $affected_keys) . "'";

        $affected_languages_str = "'" . implode("','", $affected_languages) . "'";

        $affected_uqids_str = "'" . implode("','", $affected_uqids) . "'";

        $rpQuery->query("DELETE FROM $rs_dr_translations
        WHERE `uq_id` IN ($affected_uqids_str) 
        AND `key` IN ($affected_keys_str) 
        AND `language` IN ($affected_languages_str)");

        // insert the new rows
        foreach( $compatible_budget_translations as $translation_row ) {

            $language = $translation_row['language'] ?? '';

            if( !in_array( $language, $downloaded_languages_name ) ) {

                // Skip if not downloaded language
                continue;

            }

            $key = $translation_row['key'] ?? '';

            $value = $translation_row['value'] ?? '';

            if( !empty( $language ) && !empty( $key ) && !empty( $value ) ) {

                foreach( $compatible_budget_da_uq_ids as $uq_id ) {

                    if( !empty( $uq_id ) && !empty( $language ) && !empty( $key ) && !empty( $value ) ) {

                        $rpQuery->query("INSERT INTO $rs_dr_translations (`uq_id`, `language`, `key`, `value`) VALUES ('".rp_escape_sql($uq_id)."', '".rp_escape_sql($language)."', '".rp_escape_sql($key)."', '".rp_escape_sql($value)."')");
        
                    }

                }

            }

        }

    }

    $rpQuery->query("COMMIT");

    echo json_encode(array('status' => true, 'message' => 'Required attribute names have been successfully updated.'));

    exit();

}

rp_ajax_for_admin('rp_rename_old_attributes_after_dp_v2_update', 'rp_rename_old_attributes_after_dp_v2_update');

function rp_reset_all_dp_margins() {

    if( ( $_REQUEST['confirm'] ?? 0 ) != '1' ) {

        echo json_encode(array('status' => false, 'rp_validation_error' => 'Invalid Request'));

        exit();

    }

    if( rp_current_user_has_full_access_cached() == false ) {

        echo json_encode(array('status' => false, 'rp_validation_error' => 'You do not have permission to perform this action.'));

        exit();

    }

    global $rpQuery;

    $need_to_reset = array(

        'rs_default_repair' => 'dr_margin',

        'rs_default_repair_attr' => 'da_margin',

        'rs_location_prices' => 'margin',

        'rs_repair' => 'r_margin',

        'rs_repair_attr' => 'a_margin'

    );

    $rpQuery->query("START TRANSACTION");

    foreach( $need_to_reset as $table => $column ) {

        $table_name = $rpQuery->prefix . $table;

        $rpQuery->query("UPDATE {$table_name} SET {$column} = 0");

    }

    $rpQuery->query("COMMIT");

    echo json_encode(array('status' => true, 'message' => 'All margins have been successfully reset to 0'));

    exit();

}

rp_ajax_for_admin('rp_reset_all_dp_margins', 'rp_reset_all_dp_margins');

function rp_parse_dp_margin_setting( $amount = '' ) {

    if( empty( $amount ) ) {

        return 0;

    }

    // make sure it's in valid format
    // ^\d+([.,]\d+)?$
    if( preg_match('/^\d+([.,]\d+)?$/', $amount) ) {

        $amount = str_replace(',', '.', $amount);

        $amount = (float) $amount;

        $amount = round($amount, 2);

    } else {

        $amount = 0;

    }

    return $amount;

}

function _rp_apply_custom_price_rules_on_part_price( $part_price = 0, $supplier_id = 0 ) {

    $custom_price_rules = rp_get_custom_price_rules_of_supplier( $supplier_id );

    if( empty( $part_price ) || empty( $custom_price_rules ) ) {

        return array( array(), $part_price );

    }

    $prices = array(
        'of_part_price' => $part_price
    );

    $modified_price = $part_price;

    $rules_affecting_part_price = array();

    foreach( $custom_price_rules as $rule ) {

        $floatPrice = $rule['value'] ?? 0;

        $floatPrice = str_replace(',', '.', $floatPrice);

        $floatPrice = (float) $floatPrice;

        $floatPrice = round($floatPrice, 2);

        $addOrRemoveValue = 0;

        if( $rule['type'] == 'fixed' ) {

            $addOrRemoveValue = $floatPrice;

        } else {

            $addOrRemoveValue = ( $floatPrice * $prices[ $rule['of'] ] ) / 100;

        }

        $addOrRemoveValue = round($addOrRemoveValue, 2);

        if( $rule['action'] == 'add' ) {

            $modified_price += $addOrRemoveValue;

            $rules_affecting_part_price[] = array(
                'id' => $rule['id'],
                'text' => $rule['text'],
                'amount' => $addOrRemoveValue
            );

        } else {

            $modified_price -= $addOrRemoveValue;

            $rules_affecting_part_price[] = array(
                'id' => $rule['id'],
                'text' => $rule['text'],
                'amount' => ( - ($addOrRemoveValue ) )
            );

        }

        // round to 2 decmials
        $modified_price = round($modified_price, 2);

        $prices[ $rule['id'] ] = $modified_price;

    }

    return array( $rules_affecting_part_price, $modified_price );

}

function _rp_dp_handle_custom_price_rules( $filtered = array() ) {

    if( !empty( $filtered ) ) {

        $final_filter = array();

        foreach( $filtered as $key => $row ) {

            $price_before_modification = $row->price;

            list( $rules_affecting_part_price, $row->price ) = _rp_apply_custom_price_rules_on_part_price( $row->price, $row->supplier_id );

            $row->modified_part_price = $row->price;

            $applied_rules = rp_get_custom_price_rules_of_supplier( $row->supplier_id );

            $row->applied_price_rules = $applied_rules;

            $row->rules_affecting_part_price = $rules_affecting_part_price;

            if( $price_before_modification != $row->price ) {

                $row->custom_price_rule_applied = true;

            } else {

                $row->custom_price_rule_applied = false;

            }

            $final_filter[] = $row;
            
        }

        $filtered = $final_filter;

    }

    return $filtered;

}

add_filter('rp_dp_get_skus_filtered', '_rp_dp_handle_custom_price_rules', 20, 1);

function rp_get_supplier_currency_by_supplier_id( $supplier_id = 0 ) {

    // Example Value: USD | EUR | GBP
    $plugin_currency = rp_get_selected_currency_letters();

    if( empty( $plugin_currency ) ) {

        return FALSE;

    }

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers_with_currency_data( $plugin_currency );

    if( !empty( $suppliers ) ) {

        foreach( $suppliers as $supplier ) {

            if( $supplier->id == $supplier_id ) {

                $supplier_currency = $supplier->currency ?? '€ EUR (Euro Member Countries)';
    
                $supplier_currency_three_letters = explode(' ', $supplier_currency)[1] ?? 'ERROR';

                return $supplier_currency_three_letters;

            }

        }
        
    }

    return FALSE;

}

function rp_supplier_currency_to_plugin_currency( $supplier_id = 0, $baseAmount = 0 ) {

    // Example Value: USD | EUR | GBP
    $plugin_currency = rp_get_selected_currency_letters();

    if( empty( $plugin_currency ) ) {

        return FALSE;

    }

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers_with_currency_data( $plugin_currency );

    if( !empty( $suppliers ) ) {

        foreach( $suppliers as $supplier ) {

            if( $supplier->id == $supplier_id ) {

                $supplier_currency = $supplier->currency ?? '€ EUR (Euro Member Countries)';
    
                $supplier_currency_three_letters = explode(' ', $supplier_currency)[1] ?? 'ERROR';

                $supplier_currency_three_letters = strtoupper( $supplier_currency_three_letters );

                if( $plugin_currency == $supplier_currency_three_letters ) {

                    return $baseAmount;

                } else {

                    $conversion_rate = rp_get_currency_conversion_rate( $supplier_currency_three_letters );

                    if( $conversion_rate === false ) {

                        return FALSE;

                    }

                    return rp_convert_to_plugin_currency( $baseAmount, $supplier_currency_three_letters );

                }

            }

        }
        
    }

    return FALSE;

}

function rp_convert_to_plugin_currency( $baseAmount = 0, $baseCurrency = '' ) {

    $toCurrency = rp_get_selected_currency_letters(); // plugin currency

    if( empty( $baseCurrency ) || empty( $toCurrency ) || $baseCurrency == $toCurrency ) {

        return $baseAmount;

    }

    $conversion_rate = rp_get_currency_conversion_rate( $baseCurrency );

    if( $conversion_rate === false ) {

        return $baseAmount;

    }

    $singleConvertedUnit = 1 * ( 1 / $conversion_rate );

    // Need to do proper rounding....
    if( $singleConvertedUnit >= 0.01 ) {

        $singleConvertedUnit = round( $singleConvertedUnit, 2 );

    } else {
        
        $singleConvertedUnit = round( $singleConvertedUnit, 5 );

    }

    $toAmount = $baseAmount * $singleConvertedUnit;

    // Need to do proper rounding....
    if( $toAmount >= 0.01 ) {

        $toAmount = round( $toAmount, 2 );

    } else {
        
        $toAmount = round( $toAmount, 5 );

    }
    
    return $toAmount;

}

function rp_get_decimal_setting() {

    global $rpQuery;

    $localization = $rpQuery->prefix . "rs_localization";

	$localization_data = $rpQuery->get_results("SELECT * FROM $localization");

	$decimal = ($localization_data[0]->set_decimals ?? 'comma');

    return $decimal;

}

function rp_get_currency_conversion_badge_info( $supplier_currency = '' ) {

    $plugin_currency = rp_get_selected_currency_letters();

    $conversion_rate = rp_get_currency_conversion_rate( $supplier_currency );

    if( $conversion_rate === false ) {

        return array( false, '', '' );

    }

    $fromText = '1 ' . $supplier_currency;

    $toAmount = rp_convert_to_plugin_currency( 1, $supplier_currency );

    if( rp_get_decimal_setting() == 'comma' ) {

        // replace . with ,
        $toAmount = str_replace('.', ',', $toAmount);

    }

    $toText = $toAmount . ' ' . $plugin_currency;

    return array( $conversion_rate, $fromText, $toText ); 

}

function rp_get_cached_or_download_suppliers_with_currency_data( $plugin_currency = '' ) {

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers(1800);

    if( !empty( $suppliers ) ) {
        
        $need_to_refetch = true;

        foreach($suppliers as $supplier) {

            if( !empty( $supplier ) && property_exists( $supplier, 'currency_converter_base' ) && $supplier->currency_converter_base == $plugin_currency ) {

                $need_to_refetch = false;

            }

        }

        if( $need_to_refetch === true ) {

            rp_update_option('rp_dp_last_downloaded_suppliers', FALSE);
    
            list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers(1800);
    
        }

    }

    return array($suppliers, $last_synced);

}

function rp_get_currency_conversion_rate( $currency = '' ) {

    // Example Value: USD | EUR | GBP
    $plugin_currency = rp_get_selected_currency_letters();

    if( empty( $plugin_currency ) ) {

        return FALSE;

    }

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers_with_currency_data( $plugin_currency );

    if( !empty( $suppliers ) ) {

        foreach($suppliers as $firstSupplier) {

            if( !empty( $firstSupplier ) && property_exists( $firstSupplier, 'currency_converter_base' ) && $firstSupplier->currency_converter_base == $plugin_currency ) {

                try {

                    if( !empty( $firstSupplier ) && property_exists( $firstSupplier, 'currency_converter_rates' ) ) {

                        $currency_converter_rates = json_decode( json_encode( $firstSupplier->currency_converter_rates ), TRUE );
            
                    }

                } catch( \Exception|\Error|\Throwable|\ErrorException $e ) {

                    $currency_converter_rates = array();

                }

                // loop through all and make keys uppercase for $currency_converter_rates
                $filtered = array();

                foreach( $currency_converter_rates as $key => $value ) {

                    $filtered[ strtoupper( $key ) ] = $value;

                }

                $currency_converter_rates = $filtered;

                // Also make $currency uppercase
                $currency = strtoupper( $currency );

                return array_key_exists( $currency, $currency_converter_rates) ? $currency_converter_rates[ $currency ] : false;

                break;

            } else {

                return false;

            }

        }

    }

    return false;

}

function rp_dp_get_linked_attributes_of_supplier( $supplier_id = 0, $overwrite_custom = TRUE ) {

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers(1800);

    if( !empty( $suppliers ) ) {

        $firstSupplier = FALSE;

        foreach($suppliers as $supplier) {

            if( $firstSupplier === FALSE ) {

                $firstSupplier = $supplier;

            }

            if( $supplier->id == $supplier_id ) {

                if( property_exists( $supplier, 'linked_attributes' ) ) {

                    if( !empty( $supplier->linked_attributes ) ) {

                        try {

                            $linked_attributes = json_decode( $supplier->linked_attributes, TRUE );
    
                        } catch( \Exception|\Error|\Throwable|\ErrorException $e ) {
    
                            $linked_attributes = array();
    
                        }

                    } else {

                        $linked_attributes = array();

                    }

                    if( $overwrite_custom === TRUE ) {

                        $custom_configuration = rp_get_option('rp_dp_v2_linked_attributes_' . $supplier_id, FALSE);

                        if( !empty( $custom_configuration ) ) {

                            try {

                                $linked_attributes = json_decode( $custom_configuration, TRUE );
        
                            } catch( \Exception|\Error|\Throwable|\ErrorException $e ) {
        
                                $linked_attributes = array();
        
                            }

                        }

                    }

                    if( empty( $linked_attributes ) ) {

                        return array();

                    }

                    $rp_base_attributes_for_linking = array();

                    try {

                        if( !empty( $firstSupplier ) && property_exists( $firstSupplier, 'rp_base_attributes_for_linking' ) ) {

                            $rp_base_attributes_for_linking = json_decode( json_encode( $firstSupplier->rp_base_attributes_for_linking ), TRUE );
                
                        }

                    } catch( \Exception|\Error|\Throwable|\ErrorException $e ) {

                        $rp_base_attributes_for_linking = array();

                    }

                    $prepared_linked_attributes = array();
                    
                    foreach( $rp_base_attributes_for_linking as $category => $attributes ) {

                        if( !isset( $prepared_linked_attributes[ $category ] ) ) {

                            $prepared_linked_attributes[ $category ] = array();

                        }

                        $linked_by_category = $linked_attributes[ $category ] ?? array();

                        foreach( $attributes as $attr ) {

                            $prepared_linked_attributes[ $category ][ $attr ] = $linked_by_category[ $attr ] ?? array();

                        }

                    }

                    return $prepared_linked_attributes;

                }

            }

        }

    }

    return array();

}

function rp_dp_v2_array_value_suppliers( $suppliers ) {

    $filtered = array();

    foreach($suppliers as $supplier) {

        $filtered[] = $supplier;

    }

    return $filtered;

}

function rp_dp_v2_linking_uqids_info_by_category() {

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers(1800);

    $need_to_refetch = false;

    if( empty( $suppliers ) ) {

        $need_to_refetch = true;

    }

    if( $need_to_refetch === false ) {

        $suppliers = rp_dp_v2_array_value_suppliers( $suppliers );

        $firstSupplier = $suppliers[0];

        if( !property_exists( $firstSupplier, 'dp_v2_repair_uqids_by_category' ) || !property_exists( $firstSupplier, 'dp_v2_attr_uqids_by_category') || !property_exists( $firstSupplier, 'rp_base_attributes_for_linking') ) {

            $need_to_refetch = true;

        }

    }

    if( $need_to_refetch === true ) {

        rp_update_option('rp_dp_last_downloaded_suppliers', FALSE);

        list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers(1800);

    }

    if( !empty( $suppliers ) ) {

        $suppliers = rp_dp_v2_array_value_suppliers( $suppliers );

        $dp_v2_repair_uqids_by_category = array();

        $dp_v2_attr_uqids_by_category = array();

        $rp_base_attributes_for_linking = array();

        $firstSupplier = $suppliers[0];

        try {

            if( property_exists( $firstSupplier, 'dp_v2_repair_uqids_by_category' ) ) {

                $dp_v2_repair_uqids_by_category = json_decode( json_encode( $firstSupplier->dp_v2_repair_uqids_by_category ), TRUE );
    
            }

        } catch( \Exception|\Error|\Throwable|\ErrorException $e ) {

            $dp_v2_repair_uqids_by_category = array();

        }

        try {

            if( property_exists( $firstSupplier, 'dp_v2_attr_uqids_by_category' ) ) {

                $dp_v2_attr_uqids_by_category = json_decode( json_encode( $firstSupplier->dp_v2_attr_uqids_by_category ), TRUE );
    
            }

        } catch( \Exception|\Error|\Throwable|\ErrorException $e ) {

            $dp_v2_attr_uqids_by_category = array();

        }

        try {

            if( property_exists( $firstSupplier, 'rp_base_attributes_for_linking' ) ) {

                $rp_base_attributes_for_linking = json_decode( json_encode( $firstSupplier->rp_base_attributes_for_linking ), TRUE );
    
            }

        } catch( \Exception|\Error|\Throwable|\ErrorException $e ) {

            $rp_base_attributes_for_linking = array();

        }

        return array($dp_v2_repair_uqids_by_category, $dp_v2_attr_uqids_by_category, $rp_base_attributes_for_linking);

    }

    return array(array(), array(), array());

}

function rp_get_alternative_names_of_repair_attributes_for_linking( $dp_v2_attr_uqids_by_category = array() ) {

    $prepared = array();

    global $rpQuery;

    $rs_default_repair_attr = $rpQuery->prefix . 'rs_default_repair_attr';

    $rs_dr_translations = $rpQuery->prefix . 'rs_dr_translations';

    foreach( $dp_v2_attr_uqids_by_category as $category => $attrs ) {

        if( !isset( $prepared[ $category ] ) ) {

            $prepared[ $category ] = array();

        }

        foreach( $attrs as $attr_name => $attr_uq_ids ) {

            $imploded = implode("','", $attr_uq_ids);

            $sql = "SELECT `value` FROM $rs_dr_translations
            WHERE `key` = 'attr_name' AND `language` = 'English (United States)'
            AND `uq_id` IN ('$imploded') LIMIT 1";

            $row = $rpQuery->get_row($sql);

            if( !empty( $row ) ) {

                $alternative_name = $row->value;

                if( $alternative_name != $attr_name ) {

                    $alternative_name = $attr_name . '|' . $alternative_name;

                }
                
            } else {

                $sql = "SELECT `da_name` FROM $rs_default_repair_attr
                WHERE `da_uq_id` IN ('$imploded') LIMIT 1";

                $row = $rpQuery->get_row($sql);

                if( !empty( $row ) ) {

                    $alternative_name = $row->da_name;
    
                    if( $alternative_name != $attr_name ) {
    
                        $alternative_name = $attr_name . '|' . $alternative_name;
    
                    }
                    
                } else {

                    $alternative_name = $attr_name;

                }

            }

            $prepared[ $category ][ $attr_name ] = $alternative_name;

        }

    }

    return $prepared;

}

function rp_get_data_for_dynamic_pricing_v2_attributes_linking( $supplier_id = 0 ) {

    list($dp_v2_repair_uqids_by_category, $dp_v2_attr_uqids_by_category, $rp_base_attributes_for_linking) = rp_dp_v2_linking_uqids_info_by_category();

    $valid_attributes = rp_dp_get_valid_attributes_of_supplier( $supplier_id );

    $linked_attributes = rp_dp_get_linked_attributes_of_supplier( $supplier_id );

    return array($dp_v2_repair_uqids_by_category, $dp_v2_attr_uqids_by_category, $rp_base_attributes_for_linking, $valid_attributes, $linked_attributes);

}

function rp_get_manage_dynamic_pricing_attributes_info() {

    $supplier_id = (int) ($_POST['supplier_id'] ?? 0);

    list($dp_v2_repair_uqids_by_category, $dp_v2_attr_uqids_by_category, $rp_base_attributes_for_linking, $valid_attributes, $linked_attributes) = rp_get_data_for_dynamic_pricing_v2_attributes_linking( $supplier_id );

    $alternative_names = rp_get_alternative_names_of_repair_attributes_for_linking( $dp_v2_attr_uqids_by_category );

    $show_reset_button = false;

    $custom_configuration = rp_get_option('rp_dp_v2_linked_attributes_' . $supplier_id, FALSE);

    if( !empty( $custom_configuration ) ) {

        $show_reset_button = true;

    }

    $screen_available_qualities = rp_dp_show_available_attributes_for_linking( $supplier_id, 'screen' );

    $battery_available_qualities = rp_dp_show_available_attributes_for_linking( $supplier_id, 'battery' );

    $other_available_qualities = rp_dp_show_available_attributes_for_linking( $supplier_id, 'other' );

    $originally_linked_attributes = rp_dp_get_linked_attributes_of_supplier( $supplier_id, FALSE );

    echo json_encode(array('status' => true, 'dp_v2_repair_uqids_by_category' => $dp_v2_repair_uqids_by_category, 'dp_v2_attr_uqids_by_category' => $dp_v2_attr_uqids_by_category, 'rp_base_attributes_for_linking' => $rp_base_attributes_for_linking, 'valid_attributes' => $valid_attributes, 'linked_attributes' => $linked_attributes, 'alternative_names' => $alternative_names, 'show_reset_button' => $show_reset_button, 'screen_available_qualities' => $screen_available_qualities, 'battery_available_qualities' => $battery_available_qualities, 'other_available_qualities' => $other_available_qualities, 'originally_linked_attributes' => $originally_linked_attributes));

    exit();
    
}

rp_ajax_for_admin('rp_get_manage_dynamic_pricing_attributes_info', 'rp_get_manage_dynamic_pricing_attributes_info');

function rp_reset_dp_v2_linked_attributes() {

    $supplier_id = (int) ($_POST['supplier_id'] ?? 0);

    rp_delete_option('rp_dp_v2_linked_attributes_' . $supplier_id);

    echo json_encode(array('status' => true, 'message' => 'Successfully Reset'));

    exit();

}

rp_ajax_for_admin('rp_reset_dp_v2_linked_attributes', 'rp_reset_dp_v2_linked_attributes');

function rp_edit_dp_v2_linked_attributes() {

    $supplier_id = (int) ($_POST['supplier_id'] ?? 0);

    $mapped_fields = $_POST['mapped_fields'] ?? ''; // it's json string

    if( !empty( $mapped_fields ) ) {

        // fix json string
        $mapped_fields = stripslashes( $mapped_fields );

        try {

            $mapped_fields = json_decode( $mapped_fields, true );
    
        } catch (Exception|Error $e) {
    
            $mapped_fields = array();
    
        }

    } else {

        $mapped_fields = array();

    }

    if( !empty( $supplier_id ) && !empty( $mapped_fields ) ) {

        $linked_attributes = json_encode( $mapped_fields );

        rp_update_option('rp_dp_v2_linked_attributes_' . $supplier_id, $linked_attributes);

        echo json_encode(Array('status'=> true, 'message'  => 'Successfully Updated'));

        exit();

    } else {

        echo json_encode(Array('status'=> false, 'message'  => 'Invalid Request'));

        exit();

    }

}

rp_ajax_for_admin('rp_edit_dp_v2_linked_attributes', 'rp_edit_dp_v2_linked_attributes');

function rp_get_cached_or_download_suppliers( $max_cache_time = 600 ) {

    $rp_dp_last_downloaded_suppliers = rp_get_option('rp_dp_last_downloaded_suppliers', FALSE);

    $suppliers = FALSE;
    $last_synced = FALSE;

    if( $rp_dp_last_downloaded_suppliers !== FALSE && is_array($rp_dp_last_downloaded_suppliers) && isset( $rp_dp_last_downloaded_suppliers['last_synced'] ) ) {

        if( $rp_dp_last_downloaded_suppliers['last_synced'] > ( time() - $max_cache_time ) ) {

            // it is less than 10 minutes since the last download, use the cached version
            $suppliers = $rp_dp_last_downloaded_suppliers['response'];
            $last_synced = $rp_dp_last_downloaded_suppliers['last_synced'];

        }

    }

    if( $suppliers === FALSE ) {

        // download the suppliers
        $suppliers = rp_dp_download_suppliers();

        if( !empty( $suppliers ) ) {

            $last_synced = time();

            // save the suppliers
            rp_update_option('rp_dp_last_downloaded_suppliers', array(
                'last_synced' => $last_synced,
                'response' => $suppliers
            ));

        } else {

            $suppliers = FALSE;
            $last_synced = FALSE;

        }

    }

    if( $suppliers === FALSE && $rp_dp_last_downloaded_suppliers !== FALSE && is_array($rp_dp_last_downloaded_suppliers) && isset( $rp_dp_last_downloaded_suppliers['last_synced'] ) ) {

        // download failed
        // more than 10 minutes but we have no other option, use the cached version
        $suppliers = $rp_dp_last_downloaded_suppliers['response'];
        $last_synced = $rp_dp_last_downloaded_suppliers['last_synced'];

    }

    return array($suppliers, $last_synced);

}

function rp_get_enabled_suppliers() {

    if( !RP_DynamicPricingAddon::is_active() ) {

        return array();

    }

    static $cached_suppliers = FALSE;

    if( $cached_suppliers !== FALSE ) {
        return $cached_suppliers;
    }

    list( $suppliers, $last_synced ) = rp_get_cached_or_download_suppliers();

    $rp_dp_enabled_suppliers = rp_get_option('rp_dp_enabled_suppliers', array());

    // make sure enabled suppliers are in the list of suppliers
    if( !empty( $rp_dp_enabled_suppliers ) && is_array( $rp_dp_enabled_suppliers ) && !empty( $suppliers ) ) {

        foreach($rp_dp_enabled_suppliers as $key => $supplier_id) {
            $found = false;
            foreach($suppliers as $supplier) {
                if($supplier->id == $supplier_id) {
                    $found = true;
                    break;
                }
            }
            if(!$found) {
                unset($rp_dp_enabled_suppliers[$key]);
            }
        }
        $rp_dp_enabled_suppliers = array_values($rp_dp_enabled_suppliers);

    } else {

        $rp_dp_enabled_suppliers = array();

    }

    $cached_suppliers = $rp_dp_enabled_suppliers;

    return $rp_dp_enabled_suppliers;

}

function rp_is_enabled_dynamic_pricing_fast_check() {

    static $last_dp_enabled = null;

    if( $last_dp_enabled !== null ) {

        return $last_dp_enabled;

    }

    if( !RP_DynamicPricingAddon::is_active() ) {

        $last_dp_enabled = false;

        return false;

    }

    $enabled_suppliers = rp_get_option('rp_dp_enabled_suppliers', array());

    $rp_dynamic_pricing_is_enabled = rp_get_option('rp_dynamic_pricing_is_enabled', '1');

    $final_result = $rp_dynamic_pricing_is_enabled == '1' && !empty( $enabled_suppliers );

    $last_dp_enabled = $final_result;

    return $final_result;

}

function rp_is_enabled_dynamic_pricing() {

    static $last_dp_enabled = null;

    if( $last_dp_enabled !== null ) {

        return $last_dp_enabled;

    }

    if( !RP_DynamicPricingAddon::is_active() ) {

        $last_dp_enabled = false;

        return false;

    }

    $enabled_suppliers = rp_get_enabled_suppliers();

    $rp_dynamic_pricing_is_enabled = rp_get_option('rp_dynamic_pricing_is_enabled', '1');

    $final_result =  $rp_dynamic_pricing_is_enabled == '1' && !empty( $enabled_suppliers );

    $last_dp_enabled = apply_filters('rp_filter_is_enabled_dynamic_pricing', $final_result);

    return $last_dp_enabled;

}

function rp_dp_x_minutes_ago( $time = 0 ) {

    $current_time = time();

    $time_difference = $current_time - $time;

    $minutes = round($time_difference / 60);

    if($minutes < 1) {
        $minutes = 'less than a minute ago';
    } else if( (int)$minutes === 1 ) {
        $minutes = $minutes . ' minute ago';
    } else {
        $minutes = $minutes . ' minutes ago';
    }

    return $minutes;

}


function rp_dp_download_datafeed_from_base( $page = 1 ) {

    global $rpQuery;

	require_once RP_DP_PLUGIN_PATH . 'classes/Base_API.php';

    $enabled_suppliers = rp_get_enabled_suppliers();

	$base_api = new RepairPluginPro\Base_API($rpQuery);
	
	$response = $base_api->fetch('Endpoints/fetch_dp_datafeed', array('page' => $page, 'supplier_ids' => json_encode($enabled_suppliers), 'add_product_name_and_url' => '1', 'dp_version' => 'v2', 'download_limited' => '1'), TRUE);
	
	$datafeed = array();
	
	if( isset( $response ) && is_object( $response ) && property_exists( $response, 'datafeed' ) ) {
	
		$datafeed = $response;
	
	}

    return $datafeed;

}

function rp_dp_delete_everything_after_last_id( $last_id = 0 ) {

    if( empty( $last_id ) || $last_id <= 0 ) {
        return;
    }

    global $rpQuery;

    $rs_dp_linking = $rpQuery->prefix . 'rs_dp_linking';

    $last_id = (int) $last_id;

    $rpQuery->query("DELETE FROM `$rs_dp_linking` WHERE `id` > {$last_id}");

}

function rp_dp_get_downloaded_rows_to_process( $downloaded_datafeed = array(), $id_offset = 0, $id_limit = 0 ) {

    if( empty( $downloaded_datafeed ) ) {

        return array();

    }

    // remove first row from the datafeed and consider it as $keys
    $keys = array_shift( $downloaded_datafeed );

    $index_of_id = array_search('id', $keys);

    if( $index_of_id === FALSE ) {

        return array();

    }

    $rows_to_process = array();

    foreach($downloaded_datafeed as $row) {
        
        $row = (array)$row;

        $id = $row[ $index_of_id ];

        if( $id > $id_offset && $id < $id_limit ) {

            $prepared_row = array();

            foreach( $keys as $index => $key ) {

                $prepared_row[ $key ] = $row[ $index ];

            }

            $rows_to_process[] = $prepared_row;

        }

    }

    return $rows_to_process;

}

function rp_dp_process_the_datafeed( $datafeed = array(), $offset = 0, $limit = 0) {
    
    global $rpQuery;

    $rs_dp_linking = $rpQuery->prefix . 'rs_dp_linking';
    
    // delete * from rs_dp_linking where id > $offset and id < $limit
    $rpQuery->query("DELETE FROM `$rs_dp_linking` WHERE `id` > {$offset} AND `id` < {$limit}");
    
    $ids_batches = array();

    // create batch of 500 ids
    foreach($datafeed as $row) {

        $row = (array)$row;

        $ids_batches[] = $row['id'];

        if( count($ids_batches) >= 500 ) {

            // delete * from rs_dp_linking where id in (ids_batches)
            $rpQuery->query("DELETE FROM `$rs_dp_linking` WHERE `id` IN (". implode(',', $ids_batches) .")");

            $ids_batches = array();

        }

    }

    if( !empty( $ids_batches ) ) {

        // delete * from rs_dp_linking where id in (ids_batches)
        $rpQuery->query("DELETE FROM `$rs_dp_linking` WHERE `id` IN (". implode(',', $ids_batches) .")");

        $ids_batches = array();

    }

    if( !empty( $datafeed ) ) {

        $enabled_suppliers = rp_get_enabled_suppliers();

        $updated_at = date('Y-m-d H:i:s', time());

        foreach($datafeed as $row) {

            $row = (array)$row;

            if( empty( $enabled_suppliers ) || !in_array( ($row['supplier_id'] ?? 0), $enabled_suppliers ) ) {

                continue;

            }

            $row['updated_at'] = $updated_at;

            $rpQuery->insert($rs_dp_linking, $row);

        }

    }

}


function rp_dp_do_processing() {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return;
    }

    rp_get_linked_models_count();

    global $rpQuery;

    require_once RP_DP_PLUGIN_PATH . 'classes/longProcess/load.php';

    $saveHandler = new RepairPluginPro\LongProcessSaveHandler( $rpQuery );

    $longProcess = new RepairPluginPro\LongProcess( $saveHandler, RP_DP_REPEAT_CRON_AFTER_X_SECONDS );

    $longProcess->register_process( 'step', function( $page, $longProcess ) { 

        if( ((int) $page) === 1 ) {

            $longProcess->saveHandler->delete_file_option_where('downloaded_datafeed_from_base');

            $longProcess->saveHandler->delete_option_where('downloaded_datafeed_last_id');

            $longProcess->saveHandler->save_or_update( 'downloaded_and_processed_limited', '1' );

        }

        $response = rp_dp_download_datafeed_from_base( $page );

        if( !empty( $response ) ) {

            if( property_exists( $response, 'last_id' ) && (int) $response->last_id > 0 ) {

                $offset = (int) $response->id_offset; // 100
                $limit = (int) $response->id_limit; // 201
                $last_id = (int) $response->last_id; // 188
                $datafeed = $response->datafeed;

                if( ((int) $page) === 1 ) {

                    // remove everything after last_id
                    if( $last_id > 0 ) {

                        rp_dp_delete_everything_after_last_id( $last_id );

                    }

                }

                $rows_to_process = rp_dp_get_downloaded_rows_to_process( $datafeed, 0, PHP_INT_MAX );

                if( empty( $rows_to_process ) ) {

                    $longProcess->output = "";

                    $longProcess->log_errors('Unexpected Error: Couldn\'t get rows to process from downloaded datafeed. ID Offset: ' . $offset . ' ID Limit: ' . $limit);

                    return TRUE;

                }

                // process the datafeed...
                rp_dp_process_the_datafeed( $rows_to_process, $offset, $limit );

                $limit = $limit - 1; // 200

                $end_items = $limit;

                if( $end_items > $last_id ) {
                    $end_items = $last_id;
                }

                $longProcess->output = "Downloading and processing: " . ($offset + 1) . " to " . ($end_items) . " of " . $last_id . " items";

                if( $last_id > $limit ) {

                    // we have more data to process
                    return $page + 1;

                } else {
    
                    // Clear previous options
                    $longProcess->saveHandler->delete_file_option_where('downloaded_datafeed_from_base');
    
                    $longProcess->saveHandler->delete_option_where('downloaded_datafeed_last_id');
    
                    // we have finished processing the datafeed
                    if( rp_get_option('rp_dp_finished_cron_processing', FALSE) === FALSE ) {
    
                        rp_add_option('rp_dp_finished_cron_processing', time());
    
                    } else {
    
                        rp_update_option('rp_dp_finished_cron_processing', time());
    
                    }

                    $longProcess->saveHandler->save_or_update( 'downloaded_and_processed_limited', '1' );

                    return TRUE;

                }

            } else {

                $longProcess->output = "Looks like RepairPlugin Server doesn't have any data related to enabled suppliers.";

                return TRUE;

            }

        } else {

            $longProcess->output = "";

            $longProcess->log_errors('Invalid response received from RepairPlugin Server.');

            return TRUE;

        }

    });

    $longProcess->register_process( 'step2', function( $page, $longProcess ) { 

        $downloaded_and_processed_limited = $longProcess->saveHandler->get_option('downloaded_and_processed_limited');

        if( !empty( $downloaded_and_processed_limited ) && $downloaded_and_processed_limited == '1' ) {

            $longProcess->saveHandler->delete_option_where('downloaded_and_processed_limited');

            $longProcess->output = "Datafeed has been downloaded and processed.";

            return TRUE; // We have already processed the datafeed with newer version of plugin, no need to process it again

        }

        // Below code is left to support the processing that was already in progress through older version of plugin and in the meantime, user updated to latest version of repairplugin that does limited downloading and processing...

        $downloaded_datafeed = $longProcess->saveHandler->get_file_option('downloaded_datafeed_from_base');

        if( !empty( $downloaded_datafeed ) ) {

            try {

                $downloaded_datafeed = json_decode( $downloaded_datafeed, TRUE );

            } catch( \Exception|\Error|\Throwable|\ErrorException $e ) {
                
                $longProcess->output = "";

                $longProcess->log_errors( "Error when decoding downloaded datafeed: " . $e->getMessage() . ' on line ' . $e->getLine() . ' in file ' . $e->getFile() );

                return TRUE;

            }

        }

        if( !empty( $downloaded_datafeed ) ) {

            $limit = 3000;

		    $id_offset = ($page * $limit) - $limit; // 100

            $id_limit = $id_offset + $limit + 1; // 201

            $last_id = (int) $longProcess->saveHandler->get_option('downloaded_datafeed_last_id');

            // Process Datafeed Here -- START

            $rows_to_process = rp_dp_get_downloaded_rows_to_process( $downloaded_datafeed, $id_offset, $id_limit );

            if( empty( $rows_to_process ) ) {

                $longProcess->output = "";

                $longProcess->log_errors('Unexpected Error: Couldn\'t get rows to process from downloaded datafeed. ID Offset: ' . $id_offset . ' ID Limit: ' . $id_limit);

                return TRUE;

            }

            rp_dp_process_the_datafeed( $rows_to_process, $id_offset, $id_limit );

            // Process Datafeed Here -- END

            $limit = $id_limit - 1;

            $end_items = $limit;

            if( $end_items > $last_id ) {
                $end_items = $last_id;
            }

            $longProcess->output = "Processing: " . ($id_offset + 1) . " to " . ($end_items) . " of " . $last_id . " items";

            if( $last_id > $limit ) {
                // we have more data to process
                return $page + 1;
            } else {

                // Clear previous options
                $longProcess->saveHandler->delete_file_option_where('downloaded_datafeed_from_base');

                $longProcess->saveHandler->delete_option_where('downloaded_datafeed_last_id');

                // we have finished processing the datafeed
                if( rp_get_option('rp_dp_finished_cron_processing', FALSE) === FALSE ) {

                    rp_add_option('rp_dp_finished_cron_processing', time());

                } else {

                    rp_update_option('rp_dp_finished_cron_processing', time());

                }
                return TRUE;
            }

        } else {

            $longProcess->output = "Downloaded datafeed is empty.";

            return TRUE;

        }

    });

    if( !$longProcess->has_lock_file() ) {

        $longProcess->create_lock_file();

        $is_cron_job = defined('RP_DP_DOING_CRON_JOB') && RP_DP_DOING_CRON_JOB === TRUE;

        $startTime = microtime(true);

        list($step, $page) = $longProcess->saveHandler->get_saved_step();

        $error_thrown = false;

        do {

            try {

                $longProcess->process();
    
                if( defined('RP_DP_CRON_DO_NOT_ECHO_OUTPUT') && RP_DP_CRON_DO_NOT_ECHO_OUTPUT === TRUE ) {
    
                    // Do nothing
    
                } else {
    
                    echo $longProcess->get_output() . "\n";
    
                }
    
                $longProcess->delete_lock_file();

                list($step, $page) = $longProcess->saveHandler->get_saved_step();
    
            } catch ( \Exception|\Error|\Throwable|\ErrorException $e ) {

                $error_thrown = true;
    
                $longProcess->delete_lock_file();
    
                $longProcess->log_errors( $e->getMessage() . ' on line ' . $e->getLine() . ' in file ' . $e->getFile() );
    
                throw new \Exception( $e->getMessage() );
    
            }

            if( $is_cron_job === TRUE ) {

                sleep(1);

            }

        } while( $is_cron_job === TRUE && $error_thrown === false && ( microtime(true) - $startTime ) < 30 && $step !== 'repeat' && $step !== '' );

    } else {

        echo "Process already running";

    }

}

function rp_dp_is_waiting_period() {

    // repeat after X seconds
    $repeatAfterXSeconds = RP_DP_REPEAT_CRON_AFTER_X_SECONDS;

    // get current time
    $current_time = time();

    // get last time
    $last_time = rp_get_option('rp_dp_long_process_last_time', FALSE);

    if( $last_time === FALSE ) {

        // not a waiting period because no last time was found
        return FALSE;

    }

    // check if X seconds have passed
    if( ($current_time - $last_time) < $repeatAfterXSeconds ) {

        // waiting period
        return TRUE;

    }

    // not a waiting period
    return FALSE;

}

function rp_dp_move_errors_to_options_table() {

    $option_name = 'rp_dp_errors';

    $fileName = 'errors.log';

    $filePath = RP_DP_PLUGIN_PATH . $fileName;

    // if file exists, get the file and unserialize it
    if( file_exists( $filePath ) ) {

        $file = file_get_contents( $filePath );

        if( !empty( $file ) ) {

            $errors = unserialize( $file );

            if( !empty( $errors ) ) {

                $previous_errors = json_decode( rp_get_option( $option_name, json_encode( array() ) ), TRUE );

                if( empty( $previous_errors ) ) {

                    $previous_errors = array();

                }

                // add new errors to previous errors at the end
                $errors = array_merge( $previous_errors, $errors );

                // limit to 100 errors
                $errors = array_slice( $errors, -100 );

                // save to options table
                rp_update_option( $option_name, json_encode( $errors ) );

            }

        }

    }

    if( file_exists( $filePath ) ) {

        unlink( $filePath );

    }

}

rp_add_action('rp_dynamic_pricing_initialize', 'rp_dp_move_errors_to_options_table');

function rp_dp_get_custom_configured_suppliers( $m_uq_id = '' ) {

    static $rp_dp_custom_configured_suppliers = NULL;
    
    if( $rp_dp_custom_configured_suppliers !== NULL && array_key_exists( $m_uq_id, $rp_dp_custom_configured_suppliers ) ) {

        return $rp_dp_custom_configured_suppliers[ $m_uq_id ];

    }

    if( $rp_dp_custom_configured_suppliers === NULL ) {

        $rp_dp_custom_configured_suppliers = array();

    }

    global $rpQuery;

    $rs_model = $rpQuery->prefix . 'rs_model';

    // get brand_id_fk of the model
    $brand_id_fk = $rpQuery->get_var("SELECT `brand_id_fk` FROM `$rs_model` WHERE `m_uq_id` = '{$m_uq_id}'");

    if( empty( $brand_id_fk ) ) {

        $rp_dp_get_custom_configured_suppliers[ $m_uq_id ] = array();

        return array();

    }

    $enabled_suppliers = rp_get_enabled_suppliers();

    if( empty( $enabled_suppliers ) ) {

        $rp_dp_get_custom_configured_suppliers[ $m_uq_id ] = array();

        return array();

    }

    $linked_suppliers = array();

    foreach( $enabled_suppliers as $theSupplierId ) {

        $linked_brands = rp_get_option('rp_dp_supplier_linking_' . $theSupplierId, FALSE);

        if( $linked_brands === FALSE ) {

            $linked_brands = rp_get_all_brand_ids_for_dp_supplier_linking();

        }

        if( !empty( $linked_brands ) && in_array( $brand_id_fk, $linked_brands ) ) {

            $linked_suppliers[] = $theSupplierId;

        }

    }

    $rp_dp_custom_configured_suppliers[ $m_uq_id ] = $linked_suppliers;

    return $linked_suppliers;

}

function rp_get_all_rows_related_to_m_uq_id_and_suppliers( $m_uq_id = '', $enabled_suppliers = array() ) {

    if( empty( $enabled_suppliers ) ) {

        return array();

    }

    $hash = md5( $m_uq_id . implode( ',', $enabled_suppliers ) );

    static $rp_get_all_rows_related_to_m_uq_id_and_suppliers = NULL;

    if( $rp_get_all_rows_related_to_m_uq_id_and_suppliers !== NULL && array_key_exists( $hash, $rp_get_all_rows_related_to_m_uq_id_and_suppliers ) ) {

        return $rp_get_all_rows_related_to_m_uq_id_and_suppliers[ $hash ];

    }

    if( $rp_get_all_rows_related_to_m_uq_id_and_suppliers === NULL ) {

        $rp_get_all_rows_related_to_m_uq_id_and_suppliers = array();

    }

    global $rpQuery;

    $rs_dp_linking = $rpQuery->prefix . 'rs_dp_linking';
    
    $sql = "SELECT * FROM `$rs_dp_linking` WHERE `status` = 'published' AND `fetched_status` = 'OK' AND `on_stock` = 'Y' AND `m_uq_id` = '{$m_uq_id}' AND `supplier_id` IN (" . implode( ',', $enabled_suppliers ) . ")";

    $result = $rpQuery->get_results( $sql );

    $rp_get_all_rows_related_to_m_uq_id_and_suppliers[ $hash ] = $result;

    return $result;

}

function rp_get_all_rows_related_to_m_uq_id_and_suppliers_and_repair_uqid( $m_uq_id = '', $repair_uqid = '', $enabled_suppliers = array() ) {
 
    $allRowsRelatedToModel = rp_get_all_rows_related_to_m_uq_id_and_suppliers( $m_uq_id, $enabled_suppliers );
    
    $filtered = array();

    if( !empty( $allRowsRelatedToModel ) ) {

        foreach( $allRowsRelatedToModel as $row ) {

            if( $row->repair_uqid == $repair_uqid ) {

                $filtered[] = $row;

            }

        }

    }

    return $filtered;

}

function rp_get_dynamic_pricing_information_for_linking( $supplier_id = 0 ) {

    static $cache = NULL;

    if( $cache !== NULL && array_key_exists( $supplier_id, $cache ) ) {

        return $cache[ $supplier_id ];

    }

    if( $cache === NULL ) {

        $cache = array();

    }

    $cache[ $supplier_id ] = rp_get_data_for_dynamic_pricing_v2_attributes_linking( $supplier_id );

    return $cache[ $supplier_id ];

}

function rp_find_which_type_of_repair_for_linking( $repair_uqid = '', $dp_v2_repair_uqids_by_category = array() ) {

    foreach( $dp_v2_repair_uqids_by_category as $type => $repair_uqids ) {

        if( in_array( $repair_uqid, $repair_uqids ) ) {

            return $type;

        }

    }

    return '';

}

function rp_find_which_attribute_for_linking( $attr_uqid = '', $dp_v2_attr_uqids_by_category = array() ) {

    foreach( $dp_v2_attr_uqids_by_category as $type => $attr_uqids ) {

        if( in_array( $attr_uqid, $attr_uqids ) ) {

            return $type;

        }

    }

    return '';

}

function rp_get_main_repair_uq_id_by_attr_uqid( $attr_uqid = '' ) {

    global $rpQuery;

    $rs_repair = $rpQuery->prefix . 'rs_repair';

    $rs_repair_attr = $rpQuery->prefix . 'rs_repair_attr';

    $sql = "SELECT `r_id_fk` FROM `$rs_repair_attr` WHERE `a_uq_id` = '{$attr_uqid}'";

    $row = $rpQuery->get_row( $sql );

    if( !empty( $row ) ) {

        $repair_uqid = (int) ($row->r_id_fk ?? 0);

        $sql = "SELECT `r_uq_id` FROM `$rs_repair` WHERE `r_id` = '{$repair_uqid}'";

        $row = $rpQuery->get_row( $sql );

        if( !empty( $row ) ) {

            return $row->r_uq_id ?? '';

        }

    }

    return '';

}

function rp_get_custom_price_rules_of_supplier( $supplier_id = 0 ) {

    static $cached_results = array();

    if( empty( $supplier_id ) ) {

        return array();

    }

    $supplier_id = (int) ($supplier_id ?? 0);

    if( array_key_exists( $supplier_id, $cached_results ) ) {

        return $cached_results[ $supplier_id ];

    }

    $option_key = 'rp_dp_custom_price_rules_' . $supplier_id;

    $custom_price_rules = rp_get_option($option_key, json_encode(array()));

    try {

        $custom_price_rules = json_decode( $custom_price_rules, true );

        if( JSON_ERROR_NONE !== json_last_error() ) {

            throw new Exception('Invalid JSON format');

        }

    } catch (Exception|Error $e) {

        $custom_price_rules = array();

    }

    if( empty( $custom_price_rules ) || !is_array( $custom_price_rules ) ) {

        $custom_price_rules = array();

    }

    $cached_results[ $supplier_id ] = $custom_price_rules;

    return $custom_price_rules;

}

function rp_get_custom_price_rules() {

    $supplier_id = (int) ($_POST['supplier_id'] ?? 0);

    if( empty( $supplier_id ) ) {

        echo json_encode(array('status' => false, 'message' => 'Invalid Supplier ID'));

        exit;

    }

    $custom_price_rules = rp_get_custom_price_rules_of_supplier( $supplier_id );

    echo json_encode(array('status' => true, 'message' => 'Custom Price Rules Fetched Successfully.', 'custom_price_rules' => $custom_price_rules));

    exit;

}

rp_ajax_for_admin('rp_get_custom_price_rules', 'rp_get_custom_price_rules');

function rp_update_custom_price_rules() {

    $supplier_id = (int) ($_POST['supplier_id'] ?? 0);

    if( empty( $supplier_id ) ) {

        echo json_encode(array('status' => false, 'message' => 'Invalid Supplier ID'));

        exit;

    }

    $custom_price_rules = wp_unslash($_POST['custom_price_rules'] ?? '');

    try {

        $custom_price_rules = json_decode( $custom_price_rules, true );

        if( JSON_ERROR_NONE !== json_last_error() ) {

            throw new Exception('Invalid JSON format');

        }

    } catch (Exception|Error $e) {

        echo json_encode(array('status' => false, 'message' => 'Invalid JSON format'));

        exit;

    }

    $option_key = 'rp_dp_custom_price_rules_' . $supplier_id;

    $custom_price_rules = json_encode($custom_price_rules);

    rp_update_option($option_key, $custom_price_rules);

    echo json_encode(array('status' => true, 'message' => 'Custom Price Rules Updated Successfully.'));

    exit;

}

rp_ajax_for_admin('rp_update_custom_price_rules', 'rp_update_custom_price_rules');

function rp_dp_get_live_skus( $m_uq_id = '', $repair_uqid = '' ) {

    // In Dynamic Pricing V2, it's important to have attributes
    // because of attributes linking implementation
    // So return if it isn't an attribute
    if( strpos( $repair_uqid, 'rattr_' ) === FALSE ) {

        return array();

    }

    global $rpQuery;

    $enabled_suppliers = rp_dp_get_custom_configured_suppliers( $m_uq_id );

    if( empty( $enabled_suppliers ) ) {

        return array();

    }

    $main_repair_uqid = rp_get_main_repair_uq_id_by_attr_uqid( $repair_uqid );

    if( empty( $main_repair_uqid ) ) {

        return array();

    }

    $result = rp_get_all_rows_related_to_m_uq_id_and_suppliers_and_repair_uqid( $m_uq_id, $main_repair_uqid, $enabled_suppliers );

    if( !empty( $result ) ) {

        $filtered = array();

        foreach( $result as $row ) {

            list($dp_v2_repair_uqids_by_category, $dp_v2_attr_uqids_by_category, $rp_base_attributes_for_linking, $valid_attributes, $linked_attributes) = rp_get_dynamic_pricing_information_for_linking( $row->supplier_id );

            if( empty( $linked_attributes ) ) {

                // Skip...
                continue;

            }

            if( !empty( $valid_attributes ) ) {

                $valid_attributes = array_map('trim', $valid_attributes);

            }

            if( !in_array( trim( $row->attribute ), $valid_attributes ) ) {

                // Skip...
                continue;

            }

            // screen|battery|other
            $type_of_repair = rp_find_which_type_of_repair_for_linking( $main_repair_uqid, $dp_v2_repair_uqids_by_category );

            if( empty( $type_of_repair ) ) {

                // Might be an invidiual repair
                continue;

            }

            $type_of_attribute = rp_find_which_attribute_for_linking( $repair_uqid, $dp_v2_attr_uqids_by_category[ $type_of_repair ] ?? array() );

            if( empty( $type_of_attribute ) ) {

                // Might be an invidiual attribute
                continue;

            }

            $supplier_attributes = array();

            $linked_attributes = $linked_attributes[ $type_of_repair ] ?? array();

            foreach( $linked_attributes as $ourAttr => $supplierAttrs ) {

                if( strtolower( trim( $ourAttr) ) === strtolower( trim( $type_of_attribute ) ) ) {

                    $supplier_attributes = $supplierAttrs;

                    break;

                }

            }

            if( !empty( $supplier_attributes ) && is_array( $supplier_attributes ) ) {

                $found = false;

                foreach( $supplier_attributes as $supplier_attribute ) {

                    if( strtolower( trim( $supplier_attribute ) ) === strtolower( trim( $row->attribute ) ) ) {

                            $found = true;

                            break;

                    }

                }

                if( $found === true ) {

                    $filtered[] = $row;

                }

            }

        }

        $filtered = apply_filters('rp_dp_get_skus_filtered', $filtered);

        return $filtered;

    }

    return array();

}

function _rp_handle_hiding_main_repair_if_needed( $all_repairs = array() ) {

    if( empty( $all_repairs ) || !is_array( $all_repairs ) ) {

        return $all_repairs;

    }

    $filtered = array();

    foreach( $all_repairs as $repair ) {

        $m_uq_id = get_m_uq_id_by_m_id( $repair->m_id_fk );

        $repair_uqid = $repair->r_uq_id ?? '';

        $repair->hide_if_partial_status = rp_need_to_hide_main_repair( $m_uq_id, $repair_uqid ) ? '1' : '0';

        $filtered[] = $repair;

    }
    
    return $filtered;

}

add_filter('rp_handle_hiding_main_repair_if_needed', '_rp_handle_hiding_main_repair_if_needed', 10, 1);

function rp_get_all_rows_anyway_related_to_m_uq_id_and_suppliers( $m_uq_id = '', $enabled_suppliers = array() ) {

    if( empty( $enabled_suppliers ) ) {

        return array();

    }

    $hash = md5( $m_uq_id . implode( ',', $enabled_suppliers ) );

    static $rp_get_all_rows_anyway_related_to_m_uq_id_and_suppliers = NULL;

    if( $rp_get_all_rows_anyway_related_to_m_uq_id_and_suppliers !== NULL && array_key_exists( $hash, $rp_get_all_rows_anyway_related_to_m_uq_id_and_suppliers ) ) {

        return $rp_get_all_rows_anyway_related_to_m_uq_id_and_suppliers[ $hash ];

    }

    if( $rp_get_all_rows_anyway_related_to_m_uq_id_and_suppliers === NULL ) {

        $rp_get_all_rows_anyway_related_to_m_uq_id_and_suppliers = array();

    }

    global $rpQuery;

    $rs_dp_linking = $rpQuery->prefix . 'rs_dp_linking';
    
    $sql = "SELECT * FROM `$rs_dp_linking` WHERE `status` = 'published' AND `fetched_status` = 'OK' AND (`on_stock` = 'Y' OR `on_stock` = 'N') AND `m_uq_id` = '{$m_uq_id}' AND `supplier_id` IN (" . implode( ',', $enabled_suppliers ) . ")";

    $result = $rpQuery->get_results( $sql );

    $rp_get_all_rows_anyway_related_to_m_uq_id_and_suppliers[ $hash ] = $result;

    return $result;

}

function rp_clear_front_end_steps_cache_for_dp_if_needed() {

    if( rp_get_option('rp_dp_finished_dp_processing_atleast_once', '0') != '1' ) {

        if( !empty( rp_get_option('rp_dp_finished_cron_processing', '') ) ) {

            // Date is there it means process must have finished at least once
            rp_update_option('rp_dp_finished_dp_processing_atleast_once', '1' );

        }

    }

}

add_action('init', 'rp_clear_front_end_steps_cache_for_dp_if_needed', 9999);

function rp_get_all_default_repair_uqids_cached() {

    static $cached = NULL;

    if( $cached !== NULL ) {

        return $cached;

    }

    global $rpQuery;

    $rs_default_repair = $rpQuery->prefix . 'rs_default_repair';

    $sql = "SELECT `dr_uq_id` FROM `$rs_default_repair`";

    $default_repairs = $rpQuery->get_results( $sql );

    $cached = array();

    if( !empty( $default_repairs ) ) {

        foreach( $default_repairs as $repair ) {

            $uq_id = $repair->dr_uq_id ?? '';

            if( !empty( $uq_id ) ) {

                $cached[] = $uq_id;

            }

        }

    }

    $cached = array_unique( $cached );

    $cached = array_values( $cached );

    return $cached;

}

function rp_default_repair_require_part( $dr_uq_id = '' ) {

    // get all dr_uq_id from rs_default_repair table and check if it's really a default repair
    // if it's not a default repair, it doesn't require a part
    $dr_uq_ids = rp_get_all_default_repair_uqids_cached();

    if( empty( $dr_uq_id ) || !is_string( $dr_uq_id ) || !in_array( $dr_uq_id, $dr_uq_ids ) ) {

        // Not a default repair
        return FALSE;

    }

    $doesnt_require_parts = rp_list_of_repairs_that_doesnt_have_parts();

    return !in_array( $dr_uq_id, $doesnt_require_parts ); // If it is in the list, it doesn't require parts

}

function rp_list_of_repairs_that_doesnt_have_parts() {

    static $statically_found = NULL;

    if( $statically_found !== NULL ) {

        return $statically_found; // Already found, so return it

    }

    // * Investigation
    // * Water Damage
    // * ⁠Motherboard
    // * ⁠Update or Erase Software
    // * ⁠Remove Reset Lock
    // * ⁠Change to a new device

    global $rpQuery;

    $rs_default_repair = $rpQuery->prefix . 'rs_default_repair';

    $rs_dr_translations = $rpQuery->prefix . 'rs_dr_translations';

    $sql = "SELECT * FROM $rs_default_repair";

    $default_repairs = $rpQuery->get_results( $sql );

    $prepared_uqids = array();

    foreach( $default_repairs as $repair ) {

        $uq_id = $repair->dr_uq_id ?? '';

        $dr_category = $repair->dr_category ?? 0;

        $translation_uqid = $uq_id . '_' . $dr_category;

        // get English (United States) translation for repair_name
        $sql = "SELECT * FROM $rs_dr_translations WHERE `uq_id` = '{$translation_uqid}' AND `language` = 'English (United States)' AND `key` = 'repair_name'";

        $translation = $rpQuery->get_row( $sql );

        if( !empty( $translation ) && in_array( $translation->value, array(
            'Investigation',
            'Water Damage',
            'Motherboard',
            'Update or Erase Software',
            'Remove Reset Lock',
            'Change to a new device'
        ) ) ) {

            // This repair doesn't have parts
            if( !in_array( $uq_id, $prepared_uqids ) ) {

                $prepared_uqids[] = $uq_id;

            }

        }

    }

    $statically_found = '["repair_566600","repair_272218","repair_927037","repair_197069","repair_806428","repair_680281","repair_63e0bf15b1faf","repair_63e0bf15b205d","repair_63e0bf15b223b","repair_63dd26dfb35f5","repair_63dd26dfb3702","repair_63dd26dfb37a6","repair_63dd26dfb40ac","repair_63e0ed2bd0e42","repair_63e0ed2bd0efa","repair_63e0ed2bd144e","repair_63e0ee1c80f94","repair_63e0ee1c8104f","repair_63e0ee1c81547"]';

    $statically_found = json_decode( $statically_found, TRUE );

    if( !empty( $prepared_uqids ) ) {

        foreach( $prepared_uqids as $uqid ) {

            if( !in_array( $uqid, $statically_found ) ) {

                $statically_found[] = $uqid;

            }

        }

    }

    $statically_found = array_unique( $statically_found );

    $statically_found = array_values( $statically_found );

    return $statically_found;

}

function rp_hide_repair_if() {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return ''; // Never hide

    }

    if( empty( rp_get_option('rp_dp_finished_cron_processing', '') ) ) {

        // Dynamic Pricing has never fully synced
        // So avoid hiding any repair as we do not have full data
        return '';

    }

    $hide_repair_if = rp_get_option('rp_dp_hide_repair_if', '');

    if( $hide_repair_if != 'found_but_not_in_stock' && $hide_repair_if != 'not_found' && $hide_repair_if != 'not_found_or_not_in_stock' ) {

        $hide_repair_if = '';

    }

    return $hide_repair_if;

}

function rp_need_to_hide_main_repair( $m_uq_id = '', $main_repair_uqid = '' ) {

    if( !rp_is_enabled_dynamic_pricing() ) {

        // Dynamic Pricing is not enabled, so no need to hide main repair
        return FALSE;

    }

    $hide_repair_if = rp_hide_repair_if();

    if( empty( $hide_repair_if ) ) {

        // Option selected: never hide repair
        return FALSE;

    }

    if( !rp_default_repair_require_part( $main_repair_uqid ) ) {

        // Do not hide if part is not required for this main repair
        return FALSE;

    }

    $enabled_suppliers = rp_dp_get_custom_configured_suppliers( $m_uq_id );

    $allRowsRelatedToModel = rp_get_all_rows_anyway_related_to_m_uq_id_and_suppliers( $m_uq_id, $enabled_suppliers );

    $filtered = array();

    if( !empty( $allRowsRelatedToModel ) ) {

        foreach( $allRowsRelatedToModel as $row ) {

            if( $row->repair_uqid == $main_repair_uqid ) {

                $filtered[] = $row;

            }

        }

    }

    if( $hide_repair_if == 'found_but_not_in_stock' ) {

        if( empty( $filtered ) ) {

            // Do not hide the main repair because part
            // isn't even found
            return FALSE;
    
        }

        // If we are here, it means we have found rows for this main repair
        // But we need to check if they are in stock or not
        foreach( $filtered as $row ) {

            if( strtoupper( $row->on_stock ?? '' ) === 'Y' ) {

                // Found at least one row that is in stock, so don't hide the main repair
                return FALSE;

            }

        }

        // If we are here, it means all rows are not in stock, so hide the main repair
        return TRUE;        

    } else if( $hide_repair_if == 'not_found_or_not_in_stock' ) {

        // $hide_repair_if == 'not_found_or_not_in_stock'
        if( empty( $filtered ) ) {

            // No rows found for this main repair, so hide it
            return TRUE;
    
        }

        // If we are here, it means we have found rows for this main repair
        // But we need to check if they are in stock or not
        foreach( $filtered as $row ) {

            if( strtoupper( $row->on_stock ?? '' ) === 'Y' ) {

                // Found at least one row that is in stock, so don't hide the main repair
                return FALSE;

            }

        }

        // If we are here, it means all rows are not in stock, so hide the main repair
        return TRUE;

    } else {

        // $hide_repair_if == 'not_found'
        if( empty( $filtered ) ) {

            // No rows found for this main repair, so hide it
            return TRUE;
    
        }

        // If we are here, it means we have found rows for this main repair
        return FALSE;

    }

}

function _rp_handle_hiding_main_repair_if_needed_in_last( $all_repairs = array(), $m_uq_id = '', $repair_attrs_from_db = array() ) {
    
    $filtered = array();

    foreach( $all_repairs as $repair ) {

        if( property_exists( $repair, 'hide_if_partial_status' ) && ( $repair->hide_if_partial_status ?? '' ) == '1' ) {

            if( array_key_exists( $repair->r_id, $repair_attrs_from_db ) && !empty( $repair_attrs_from_db[ $repair->r_id ] ) ) {

                // Attributes found
                $anyAttributeHasPrice = false;

                $attributes = $repair_attrs_from_db[ $repair->r_id ] ?? array();

                foreach( $attributes as $attr ) {

                    if( !empty( $attr->a_price ) && (float) $attr->a_price > 0 ) {

                        $anyAttributeHasPrice = true;

                        break; // No need to check further, we found an attribute with price

                    }

                }

                if( $anyAttributeHasPrice === true ) {

                    $filtered[] = $repair; // Keep it in the list because at least one attribute has a price

                } else {

                    // No attributes with price, so hide this main repair
                    // Do not add it to the filtered list

                }

            } else {

                // It's a main repair without any attributes
                if( !empty( $repair->r_price ) && (float) $repair->r_price > 0 ) {

                    $filtered[] = $repair; // Keep it in the list because it has a price

                }

            }

        } else {

            $filtered[] = $repair;

        }

    }

    return $filtered;

}

add_filter('rp_filter_all_repairs_before_return', '_rp_handle_hiding_main_repair_if_needed_in_last', PHP_INT_MAX, 3);

function rp_dp_get_skus( $m_uq_id = '', $repair_uqid = '', $main_repair_uqid = '' ) {

    static $cache = NULL;

    $hash = md5( $m_uq_id . $repair_uqid . $main_repair_uqid );

    if( $cache !== NULL && array_key_exists( $hash, $cache ) ) {

        return $cache[ $hash ];

    }

    if( $cache === NULL ) {

        $cache = array();

    }

    // After dynamic pricing v2 update,
    // We are only doing linking based on attributes...
    if( strpos( $repair_uqid, 'rattr_' ) === FALSE ) {

        $cache[ $hash ] = array();

        return array();

    }

    global $rpQuery;

    $rs_dp_linking = $rpQuery->prefix . 'rs_dp_linking';

    $enabled_suppliers = rp_dp_get_custom_configured_suppliers( $m_uq_id );

    if( empty( $enabled_suppliers ) ) {

        $cache[ $hash ] = array();

        return array();

    }

    // $m_uq_id = rp_escape_sql( $m_uq_id );

    // $repair_uqid = rp_escape_sql( $repair_uqid );

    // $sql = "SELECT `price`, `sku`, `supplier_id`, `supplier_name` FROM `$rs_dp_linking` WHERE `m_uq_id` = '{$m_uq_id}' AND `repair_uqid` = '{$repair_uqid}' AND `status` = 'published' AND `fetched_status` = 'OK' AND `on_stock` = 'Y' AND `supplier_id` IN (" . implode( ',', $enabled_suppliers ) . ")";

    // $result = $rpQuery->get_results( $sql );

    $result = rp_get_all_rows_related_to_m_uq_id_and_suppliers_and_repair_uqid( $m_uq_id, $main_repair_uqid, $enabled_suppliers );

    if( !empty( $result ) ) {

        $filtered = array();

        foreach( $result as $row ) {

            list($dp_v2_repair_uqids_by_category, $dp_v2_attr_uqids_by_category, $rp_base_attributes_for_linking, $valid_attributes, $linked_attributes) = rp_get_dynamic_pricing_information_for_linking( $row->supplier_id );

            if( empty( $linked_attributes ) ) {

                // Skip...
                continue;

            }

            if( !empty( $valid_attributes ) ) {

                $valid_attributes = array_map('trim', $valid_attributes);

            }

            if( !in_array( trim( $row->attribute ), $valid_attributes ) ) {

                // Skip...
                continue;

            }

            // screen|battery|other
            $type_of_repair = rp_find_which_type_of_repair_for_linking( $main_repair_uqid, $dp_v2_repair_uqids_by_category );

            if( empty( $type_of_repair ) ) {

                // Might be an invidiual repair
                continue;

            }

            $type_of_attribute = rp_find_which_attribute_for_linking( $repair_uqid, $dp_v2_attr_uqids_by_category[ $type_of_repair ] ?? array() );

            if( empty( $type_of_attribute ) ) {

                // Might be an invidiual attribute
                continue;

            }

            $supplier_attributes = array();

            $linked_attributes = $linked_attributes[ $type_of_repair ] ?? array();

            foreach( $linked_attributes as $ourAttr => $supplierAttrs ) {

                if( strtolower( trim( $ourAttr) ) === strtolower( trim( $type_of_attribute ) ) ) {

                    $supplier_attributes = $supplierAttrs;

                    break;

                }

            }

            if( !empty( $supplier_attributes ) && is_array( $supplier_attributes ) ) {

                $found = false;

                foreach( $supplier_attributes as $supplier_attribute ) {

                    if( strtolower( trim( $supplier_attribute ) ) === strtolower( trim( $row->attribute ) ) ) {

                        $found = true;

                        break;

                    }

                }

                if( $found === true ) {

                    $filtered[] = $row;

                }

            }

        }

        $filtered = apply_filters('rp_dp_get_skus_filtered', $filtered);

        $cache[ $hash ] = $filtered;

        return $filtered;

    }

    $cache[ $hash ] = array();

    return array();

}

function rp_dp_cron_job_handler() {

    global $rpQuery;

    $rpQuery->disable_smart_cache();

    rp_update_option( 'rp_last_dynamic_pricing_cron_run', time() );

    if( rpbkp_is_running_any_process() == true ) {

        echo 'Running Import/Export process. Please wait...';

        exit;

    }

    if( rp_get_option('rp_which_way_for_processing_datafeed', 'wordpress') !== 'cron' ) {

        echo 'CRON Job option is disabled.';

        exit;

    }

    if( !defined('RP_DP_DOING_CRON_JOB') || !defined('WP_REPAIR_PLUGIN_FILE') ) {

        return;

    }

    rp_dp_do_processing();

    exit;

}

function rp_dp_cron_job_using_wordpress() {

    global $rpQuery;

    $rpQuery->disable_smart_cache();

    rp_update_option( 'rp_last_dynamic_pricing_wp_cron_run', time() );

    if( rpbkp_is_running_any_process() == true ) {

        echo 'Running Import/Export process. Please wait...';

        exit;

    }

    if( !rp_is_enabled_dynamic_pricing() ) {

        echo 'Dynamic Pricing is disabled.';

        exit;

    }

    if( rp_dp_is_waiting_period() ) {

        echo 'Waiting period.';

        exit;

    }

    if( !isset( $_POST['rp_dp_run_process'] ) ) {

        echo 'Invalid request.';

        exit;

    }

    if( rp_get_option('rp_which_way_for_processing_datafeed', 'wordpress') !== 'wordpress' ) {

        echo 'Syncronization using Wordpress option is disabled.';
        
        exit;

    }

    RepairPluginPro\rp_cron_job_verifiy(array(
        'rp_token_error' => 'No token detected?'
    ));

    define('RP_DP_CRON_DO_NOT_ECHO_OUTPUT', TRUE);

    rp_dp_do_processing();

    if( rp_dp_is_waiting_period() ) {

        echo 'Waiting period.';

    } else {

        RepairPluginPro\rp_cron_job_response(array('status' => 'success'));

    }

    exit;

}

function _run_rp_dp_cron_job_handler_through_cron() {

    if( isset( $_REQUEST['action'] ) && ( $_REQUEST['action'] ?? '' ) == 'rp_dp_cron_job_handler' ) {

        if( defined('RP_DP_DOING_CRON_JOB') && RP_DP_DOING_CRON_JOB === TRUE ) {
         
            rp_dp_cron_job_handler();

        }

    }

}

rp_add_action('init', '_run_rp_dp_cron_job_handler_through_cron', 9999);

rp_ajax_for_public('rp_dp_cron_job_using_wordpress', 'rp_dp_cron_job_using_wordpress');


function rp_handle_dynamic_pricing_processing() {

    if( !rp_is_enabled_dynamic_pricing() || rp_get_option('rp_which_way_for_processing_datafeed', 'wordpress') !== 'wordpress' || rp_dp_is_waiting_period() ) {

        return;

    }

    ?>
    <script type="text/javascript">
        window.repeatableDynamicPricingProcessingAR = false;
        function repeatableDynamicPricingProcessing(rp_token = false) {
            let theData;
            var theTime = window._getCurrentTime();
            if(rp_token === true) {
                theData = {
                    action: 'rp_dp_cron_job_using_wordpress',
                    rp_run_token: true,
                    rp_dp_run_process: 'true'
                }
            } else {
                theData = {
                    action: 'rp_dp_cron_job_using_wordpress',
                    rp_dp_run_process: 'true'
                }
            }
            window.repeatableDynamicPricingProcessingAR = true;
            let ajaxHandler = jQuery;
            if( 'repairPluginUI' in window ) {
                ajaxHandler = repairPluginUI;
            }
            ajaxHandler.ajax({
                type: "POST",
                url: '<?php echo rp_get_admin_ajax_url_for_back_end(); ?>?time='+theTime,
                data: theData,
                success: function(data) {
                    try {
                        data = JSON.parse(data);
                    } catch (e) {

                    }
                    if(data && data.rp_token_error) {
                        if(data.rp_try_after && data.rp_try_after !== false && data.rp_try_after > 0) {
                            window.setTimeout(function(){
                                repeatableDynamicPricingProcessing(true);
                            }, window._getNextSafeSchedule(data.rp_try_after));
                        }
                    } else if(data && data.rp_try_after && data.rp_try_after !== false && data.rp_try_after > 0) {
                        window.setTimeout(function(){
                            repeatableDynamicPricingProcessing(true);
                        }, window._getNextSafeSchedule(data.rp_try_after));
                    } else {
                      window.repeatableDynamicPricingProcessingAR = false;
                    }
                },
                error: function() {
                    window.repeatableDynamicPricingProcessingAR = false;
                }
            });
        }
        window._repeatableDynamicPricingProcessing = function(rp_token = false) {
            repeatableDynamicPricingProcessing(rp_token);
        }
        jQuery(window).on("load", function(){
            window.setTimeout(function(){
                repeatableDynamicPricingProcessing();
            }, window._getNextSafeSchedule(1));
        });
    </script>
    <?php
}
rp_add_action('admin_footer','rp_handle_dynamic_pricing_processing', 11);
rp_add_action('wp_footer','rp_handle_dynamic_pricing_processing', 11);



function rp_dp_human_readable_difference( $date1 = '', $date2 = '' ) {

    $diff = new \DateTime( $date1 );
    $now = new \DateTime( $date2 );
    $interval = $now->diff( $diff );

    // year, month, day, hour, minute, second
    $y = $interval->y;
    $m = $interval->m;
    $d = $interval->d;
    $h = $interval->h;
    $i = $interval->i;
    $s = $interval->s;

    $response = '';
    
    if( $y > 0 ) {
        $response .= $y . ' year';
        if( $y > 1 ) {
            $response .= 's';
        }
        $response .= ' ';
        return $response;
    }

    if( $m > 0 ) {
        $response .= $m . ' month';
        if( $m > 1 ) {
            $response .= 's';
        }
        $response .= ' ';
        return $response;
    }

    if( $d > 0 ) {
        $response .= $d . ' day';
        if( $d > 1 ) {
            $response .= 's';
        }
        $response .= ' ';
        return $response;
    }

    if( $h > 0 ) {
        $response .= $h . ' hour';
        if( $h > 1 ) {
            $response .= 's';
        }
        $response .= ' ';
        return $response;
    }

    if( $i > 0 ) {
        $response .= $i . ' minute';
        if( $i > 1 ) {
            $response .= 's';
        }
        $response .= ' ';
        return $response;
    }

    if( $s > 0 ) {
        $response .= $s . ' second';
        if( $s > 1 ) {
            $response .= 's';
        }
        $response .= ' ';
        return $response;
    }

    return $response ?? 'Just now';

}

function rp_filter_default_repair_price_th( $th = '' ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $th;
    }

    return str_replace('Price', 'Fallback', $th) . '<th style="width: 75px"><p>Margin</p></th>';

}

add_filter('rp_default_repair_price_th', 'rp_filter_default_repair_price_th', 10, 1);

function rp_filter_individual_repair_price_th( $th = '' ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $th;
    }

    return str_replace('Price', 'Fallback', $th) . '<th style="width: 75px"><p>Margin</p></th>';

}

add_filter('rp_individual_repair_price_th', 'rp_filter_individual_repair_price_th', 10, 1);

function rp_get_lowest_default_repair_attribute_margin($dr_id = 0) {

      global $rpQuery;

      global $rp_location_based_price_id;

      $rs_default_repair_attr = $rpQuery->prefix . 'rs_default_repair_attr';

      $result = $rpQuery->get_results("SELECT * FROM `$rs_default_repair_attr` WHERE `dr_id_fk` = '".rp_escape_sql($dr_id)."'");

        $lowest_price = FALSE;
        $lowest_margin = FALSE;

      foreach ($result as $row) {

            if( isset( $rp_location_based_price_id ) && !empty( $rp_location_based_price_id ) ) {

                $row = apply_filters('rp_overwrite_default_repair_attr_row', $row, $rp_location_based_price_id);

            }
        
            $thePrice = (float) str_replace( ',', '.', $row->da_price );
            $theMargin = (float) str_replace( ',', '.', ($row->da_margin ?? 0) );
            if( $lowest_price === FALSE ) {
                $lowest_price = $thePrice;
                $lowest_margin = $theMargin;
            } else if( $thePrice < $lowest_price ) {
                $lowest_price = $thePrice;
                $lowest_margin = $theMargin;
            } else {

            }

      }

      return $lowest_margin;

}

function rp_get_lowest_repair_attribute_margin($r_id = 0) {

      global $rp_location_based_price_id;

      global $rpQuery;

      $rs_repair_attr = $rpQuery->prefix . 'rs_repair_attr';

      $result = $rpQuery->get_results("SELECT * FROM `$rs_repair_attr` WHERE `r_id_fk` = '".rp_escape_sql($r_id)."'");

      $lowest_price = FALSE;
      $lowest_margin = 0;

      foreach ($result as $row) {

            if( isset( $rp_location_based_price_id ) && !empty( $rp_location_based_price_id ) ) {
                
                $row = apply_filters('rp_overwrite_individual_repair_attr_row', $row, $rp_location_based_price_id);

            }
            
            $thePrice = (float) str_replace( ',', '.', $row->a_price );
            $theMargin = (float) str_replace( ',', '.', ($row->a_margin ?? 0) );
            if( $lowest_price === FALSE ) {
                $lowest_price = $thePrice;
                $lowest_margin = $theMargin;
            } else if( $thePrice < $lowest_price ) {
                $lowest_price = $thePrice;
                $lowest_margin = $theMargin;
            } else {

            }

      }

      return $lowest_margin;

}


function rp_filter_default_repair_price_td( $td = '', $passData = array() ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $td;
    }

    // extract the data
    extract( $passData );

    $r->dr_margin = $r->dr_margin ?? 0;

    if(!empty($r_attr)) {

        $r->dr_margin = rp_get_lowest_default_repair_attribute_margin($r->dr_id);

    }

    if($decimal == 'comma'){
        $amount = str_replace('.',',',$r->dr_margin);
    }else{
        $amount = $r->dr_margin;		
    }

    $readonly = '';

    if($r->is_free === '1' || !empty($r_attr)) {
        $readonly = 'readonly="readonly"';
    }

    $to_be_filtered_td = '<td style="width: 75px"><input '.$readonly.' class="repair-price" id="repair-margin'.$m_id.'" type="text"  placeholder="99,99" name="rmargin[]" value="'.$amount.'" required></td>';

    return $td . $to_be_filtered_td;

}

add_filter('rp_default_repair_price_td', 'rp_filter_default_repair_price_td', 10, 2);

function rp_filter_individual_repair_price_td( $td = '', $passData = array() ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $td;
    }

    // extract the data
    extract( $passData );

    $r->r_margin = $r->r_margin ?? 0;

    if(!empty($r_attr)) {

        $r->r_margin = rp_get_lowest_repair_attribute_margin($r->r_id);

    }

    if($decimal == 'comma'){
        $amount = str_replace('.',',',$r->r_margin);
    }else{
        $amount = $r->r_margin;		
    }

    $readonly = '';

    if($r->is_free === '1' || !empty($r_attr)) {
        $readonly = 'readonly="readonly"';
    }

    $to_be_filtered_td = '<td style="width: 75px"><input '.$readonly.' class="repair-price" id="repair-margin'.$m_id.'" type="text"  placeholder="99,99" name="rmargin[]" value="'.$amount.'" required></td>';

    return $td . $to_be_filtered_td;

}

add_filter('rp_individual_repair_price_td', 'rp_filter_individual_repair_price_td', 10, 2);

function rp_filter_default_repair_attr_price_td( $td = '', $passData = array() ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $td;
    }

    // extract the data
    extract( $passData );

    $ra->da_margin = $ra->da_margin ?? 0;

    if($decimal == 'comma'){
        $amount = str_replace('.',',',$ra->da_margin);
    }else{
        $amount = $ra->da_margin;			
    }

    $to_be_filtered_td = '<td style="width: 75px;"><input class="repair-price" type="text" id="ramargin'.$m_id.'" placeholder="99,99" name="ramargin[]" value="'.$amount.'" required></td>';

    return $td . $to_be_filtered_td;

}

add_filter('rp_default_repair_attr_price_td', 'rp_filter_default_repair_attr_price_td', 10, 2);

function rp_filter_individual_repair_attr_price_td( $td = '', $passData = array() ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $td;
    }

    // extract the data
    extract( $passData );

    $ra->a_margin = $ra->a_margin ?? 0;

    if($decimal == 'comma'){
        $amount = str_replace('.',',',$ra->a_margin);
    }else{
        $amount = $ra->a_margin;			
    }

    $to_be_filtered_td = '<td style="width: 75px;"><input class="repair-price" type="text" id="ramargin'.$m_id.'" placeholder="99,99" name="ramargin[]" value="'.$amount.'" required></td>';

    return $td . $to_be_filtered_td;

}

add_filter('rp_individual_repair_attr_price_td', 'rp_filter_individual_repair_attr_price_td', 10, 2);


function rp_before_returning_default_repair_tr( $tr = array(), $c_repair = null ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $tr;
    }

    $tr['repair_margin'] = $c_repair->dr_margin ?? 0;

    if( !empty( $tr['attrs'] ) ) {

        $tr['repair_margin'] = rp_get_lowest_default_repair_attribute_margin( $c_repair->dr_id );

    }

    return $tr;

}

add_filter('rp_before_returning_default_repair_tr', 'rp_before_returning_default_repair_tr', 10, 2);

function rp_before_returning_default_repair_attr( $attr = array(), $c_attr = null ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $attr;
    }

    $attr['a_margin'] = $c_attr->da_margin ?? 0;

    return $attr;

}

add_filter('rp_before_returning_default_repair_attr', 'rp_before_returning_default_repair_attr', 10, 2);

function rp_before_returning_individual_repair_tr( $tr = array(), $c_repair = null ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $tr;
    }

    $tr['repair_margin'] = $c_repair->r_margin ?? 0;

    if( !empty( $tr['attrs'] ) ) {

        $tr['repair_margin'] = rp_get_lowest_repair_attribute_margin( $c_repair->r_id );

    }

    return $tr;

}

add_filter('rp_before_returning_individual_repair_tr', 'rp_before_returning_individual_repair_tr', 10, 2);

function rp_before_returning_individual_repair_attr( $attr = array(), $c_attr = null ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $attr;
    }

    $attr['a_margin'] = $c_attr->a_margin ?? 0;

    return $attr;

}

add_filter('rp_before_returning_individual_repair_attr', 'rp_before_returning_individual_repair_attr', 10, 2);

function rp_update_model_linked_count_cache_of_50_model( $m_uq_ids = array(), $limit = 50 ) {

    static $itWorked = false;

    if( $itWorked ) {
        return;
    }

    $itWorked = true;

    $cached = rp_get_option('rp_dp_model_linked_total_count_cache', json_encode(array()));

    $cached = json_decode( $cached, TRUE );

    if( empty( $cached ) ) {

        $cached = array();

    } else {

        // usort cached items by time (oldest first)
        uasort($cached, function($a, $b) {
            return $a['time'] <=> $b['time'];
        });

    }

    // loop through all m_uq_ids, if not found in cached, add it to cached
    $not_found = array();

    foreach( $m_uq_ids as $m_uq_id ) {

        if( !isset( $cached[$m_uq_id] ) ) {

            $not_found[] = $m_uq_id;        

        }

    }

    // items to process
    $items_to_process = array();

    // loop through all not_found and add to items_to_process
    foreach( $not_found as $m_uq_id ) {

        $items_to_process[] = $m_uq_id;

    }

    // loop through all m_uq_ids in cached and add to items_to_process
    foreach( $cached as $m_uq_id => $row ) {

        $items_to_process[] = $m_uq_id;

    }

    // array slice 
    $items_to_process = array_slice( $items_to_process, 0, $limit );

    // find rp_dp_check_if_model_has_any_item_linked
    foreach( $items_to_process as $m_uq_id ) {

        $result = rp_dp_check_if_model_has_any_item_linked( $m_uq_id ) == TRUE ? '1' : '0';

        $cached[$m_uq_id] = array(
            'result' => $result,
            'time' => time()
        );

    }

    // encode and save to options table
    $cached = json_encode( $cached );

    // update rp_dp_model_linked_total_count_cache
    rp_update_option('rp_dp_model_linked_total_count_cache', $cached);

}

function rp_dp_check_if_model_has_any_item_linked_cached( $m_uq_id = '' ) {

    // return from cached results
    $cached = rp_get_option('rp_dp_model_linked_total_count_cache', json_encode(array()));

    $cached = json_decode( $cached, TRUE );

    if( isset( $cached[$m_uq_id] ) ) {

        return $cached[$m_uq_id]['result'] == '1' ? TRUE : FALSE;

    }

    return FALSE;

}

function rp_dp_check_if_model_has_any_item_linked( $m_uq_id = '' ) {

    global $rpQuery;

    $rs_model = $rpQuery->prefix . 'rs_model';

    $rs_repair = $rpQuery->prefix . 'rs_repair';

    $rs_repair_attr = $rpQuery->prefix . 'rs_repair_attr';

    $rs_dp_linking = $rpQuery->prefix . 'rs_dp_linking';

    // select * from rs_model where m_uq_id = $m_uq_id

    $model = $rpQuery->get_results("SELECT m_id FROM `$rs_model` WHERE `m_uq_id` = '$m_uq_id'");

    if( empty( $model ) ) {
        return FALSE;
    }

    $model = $model[0];

    // select * from rs_repair where m_id = $model->m_id

    $repairs = $rpQuery->get_results("SELECT * FROM `$rs_repair` WHERE `m_id_fk` = '$model->m_id'");

    if( empty( $repairs ) ) {
        return FALSE;
    }

    $repair_uq_ids = array();

    $all_repair_ids = array();

    foreach( $repairs as $repairRow ) {

        $repair_uq_ids[] = $repairRow->r_uq_id;

        $all_repair_ids[] = $repairRow->r_id;

    }

    if( !empty( $all_repair_ids ) ) {

        // now find the repair attributes

        $attrs = $rpQuery->get_results("SELECT * FROM `$rs_repair_attr` WHERE `r_id_fk` IN ('" . implode("','", $all_repair_ids) . "')");

        if( !empty( $attrs ) ) {

            foreach( $attrs as $attrRow ) {

                $repair_uq_ids[] = $attrRow->a_uq_id;
    
            }
            
        }

    }

    // now check if m_uq_id and repair_uqid matches in rs_dp_linking

    $linked_models = $rpQuery->get_results("SELECT id FROM `$rs_dp_linking` WHERE `m_uq_id` = '$m_uq_id' AND `repair_uqid` IN ('" . implode("','", $repair_uq_ids) . "') AND `status` = 'published' AND `fetched_status` = 'OK' LIMIT 1");

    if( empty( $linked_models ) ) {
        return FALSE;
    }

    return TRUE;

}

function rp_get_linked_models_count() {

    global $rpQuery;

    $rs_model = $rpQuery->prefix . 'rs_model';

    $rs_repair = $rpQuery->prefix . 'rs_repair';

    $rs_repair_attr = $rpQuery->prefix . 'rs_repair_attr';

    $rs_dp_linking = $rpQuery->prefix . 'rs_dp_linking';

    // select * from rs_dp_linking GROUP BY m_uq_id

    $enabled_suppliers = rp_get_enabled_suppliers();

    if( empty( $enabled_suppliers ) ) {

        return 0;

    }

    $m_uq_ids = $rpQuery->get_results("SELECT `m_uq_id` FROM `$rs_dp_linking` 
    WHERE `supplier_id` IN (".implode(',', $enabled_suppliers).")
    AND `status` = 'published' AND `fetched_status` = 'OK' 
    GROUP BY `m_uq_id`");

    if( empty( $m_uq_ids ) ) {

        return 0;

    }

    $found_linking = 0;

    $only_m_uq_ids = array();

    foreach( $m_uq_ids as $row ) {

        $only_m_uq_ids[] = $row->m_uq_id;

    }

    rp_update_model_linked_count_cache_of_50_model( $only_m_uq_ids, 50 );

    foreach( $m_uq_ids as $row ) {

        $result = rp_dp_check_if_model_has_any_item_linked_cached( $row->m_uq_id );

        if( $result ) {

            $found_linking++;

        }

    }

    return $found_linking;

}

function rp_get_total_models_linked() {

    global $rpQuery;

    $rs_model = $rpQuery->prefix . 'rs_model';

    // select COUNT(*) FROM rs_model

    $result = $rpQuery->get_results("SELECT COUNT(*) AS total FROM `$rs_model`");

    if( empty( $result ) ) {
        return array(0,0);
    }

    $total_models = $result[0]->total;

    $linked_models_count = rp_get_linked_models_count();

    return array($total_models,$linked_models_count);

}

function _rp_manage_changed_margin_fields( $final_result = TRUE, $attr_rows = array(), $dr_attr_rows = array() ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $final_result;
    }

    if( $final_result === FALSE ) {
        return FALSE;
    }

    $uq_ids = array();
    $margin = array();

    foreach( $dr_attr_rows as $dr_attr_row ) {

          $uq_ids[] = $dr_attr_row->da_uq_id;
          $margin[] = $dr_attr_row->da_margin;

    }

    $results = array();

    foreach( $attr_rows as $attr_row ) {

        if( !in_array( $attr_row->a_uq_id, $uq_ids ) ) {

            if( rp_current_user_has_full_access_cached() == true ) {

                return FALSE;

            } else {

                $results[] = TRUE;

            }

        } else {

            $results[] = $margin[array_search($attr_row->a_uq_id, $uq_ids)] == $attr_row->a_margin;

        }

    }

    return array_search(FALSE, $results) === FALSE;

}

add_filter('rp_attributes_exactly_matches_default_with_price', '_rp_manage_changed_margin_fields', 10, 3);

function _rp_manage_changed_margin_fields_2( $final_result , $row = null, $dr_row = null) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $final_result;
    }

    if( $final_result === FALSE ) {
        return FALSE;
    }

    return ($row->r_margin == $dr_row->dr_margin);

}

add_filter('rp_is_fully_unchanged_default_repair_individually', '_rp_manage_changed_margin_fields_2', 10, 3);

function _rp_also_reset_margin( $result = FALSE ) {

    if( !rp_is_enabled_dynamic_pricing() ) {
        return $result;
    }

    return TRUE;

}

add_filter('rp_reset_default_repair_individually_margin', '_rp_also_reset_margin', 10, 1);

function _rp_add_margin_field_while_adding_repair( $dr_newdata = array(), $dr_repair = null ) {

    $dr_newdata['r_margin'] = $dr_repair->dr_margin;

    return $dr_newdata;

}

add_filter('rp_processing_repair_for_manually_added_model', '_rp_add_margin_field_while_adding_repair', 10, 2);

function _rp_also_process_attr_margin( $result = FALSE ) {

    return TRUE;

}

add_filter('rp_process_attr_margin_for_manually_added_model', '_rp_also_process_attr_margin', 10, 1);

function rp_reset_margin_field_while_default_reset( $result = FALSE ) {

    return TRUE;

}

add_filter('rp_reset_margin_field_while_default_reset', 'rp_reset_margin_field_while_default_reset', 10, 1);

function rp_find_average_price_sku_row( $skus = array() ) {

    if( count( $skus ) == 1 ) {

        $skus[0]->processed_price = $skus[0]->price;

        return $skus[0];
    }

    $total_price = 0;

    foreach( $skus as $sku ) {

        $total_price += $sku->price;

    }

    $average_price = round( $total_price / count( $skus ), 2 );

    // now loop through skus again and find the one which is closest to average price

    $closest_sku = FALSE;

    foreach( $skus as $sku ) {

        if( $sku->price <= $average_price ) {

            if( $closest_sku === FALSE ) {

                $closest_sku = $sku;

            } else {

                if( $sku->price > $closest_sku->price ) {

                    // if this sku is closer to average price than previous one
                    // then overwrite previous one

                    $closest_sku = $sku;

                }

            }

        }

    }

    // overwrite closest sku price with average price

    $closest_sku->processed_price = $average_price;

    return $closest_sku;

}

function rp_find_highest_price_sku_row( $skus = array() ) {

    if( count( $skus ) == 1 ) {

        $skus[0]->processed_price = $skus[0]->price;

        return $skus[0];
    }

    $highest_price = FALSE;

    foreach( $skus as $sku ) {

        if( $highest_price === FALSE ) {

            $highest_price = $sku;

        } else {

            if( $sku->price > $highest_price->price ) {

                $highest_price = $sku;

            }

        }

    }

    $highest_price->processed_price = $highest_price->price;

    return $highest_price;

}

function rp_find_lowest_price_sku_row( $skus = array() ) {

    if( count( $skus ) == 1 ) {

        $skus[0]->processed_price = $skus[0]->price;

        return $skus[0];

    }

    $lowest_price = FALSE;

    foreach( $skus as $sku ) {

        if( $lowest_price === FALSE ) {

            $lowest_price = $sku;

        } else {

            if( $sku->price < $lowest_price->price ) {

                $lowest_price = $sku;

            }

        }

    }

    $lowest_price->processed_price = $lowest_price->price;

    return $lowest_price;

}

function rp_dp_get_tax_rate() {

    static $cached = FALSE;

    if( $cached !== FALSE ) {

        return $cached;

    }

    global $rpQuery;

    $rs_localization = $rpQuery->prefix . "rs_localization"; 

    $tax_row = $rpQuery->get_row("SELECT * FROM $rs_localization LIMIT 1;");

    if( !empty( $tax_row ) ) {

        $cached = $tax_row->tax;

        return $tax_row->tax;

    }

    $cached = 0;

    return 0;

}

function rp_repair_has_any_attributes( $r_id = 0 ) {

    global $rpQuery;

    $rs_repair_attr = $rpQuery->prefix . 'rs_repair_attr';

    $sql = "SELECT * FROM `$rs_repair_attr` WHERE r_id_fk = '".rp_escape_sql($r_id)."' LIMIT 1;";

    $repairAttr = $rpQuery->get_results( $sql );

    if( !empty( $repairAttr ) ) {

        return true;

    }

    return false;

}

function rp_get_active_repair_attributes( $r_id = 0 ) {

    global $rpQuery;

    $rs_repair_attr = $rpQuery->prefix . 'rs_repair_attr';

    $sql = "SELECT * FROM `$rs_repair_attr` WHERE r_id_fk = '".rp_escape_sql($r_id)."' AND is_active = '1';";

    $repairAttr = $rpQuery->get_results( $sql );

    if( !empty( $repairAttr ) ) {

        return $repairAttr;

    }

    return array();

}

function rp_get_only_visible_attributes( $repairRow = null ) {
    
    global $rpQuery;

    global $rp_pre_selected_location;

    $rs_repair_attr = $rpQuery->prefix . 'rs_repair_attr';

    // $sql = "SELECT * FROM `$rs_repair_attr` WHERE r_id_fk = '".rp_escape_sql($repairRow->r_id)."' AND is_active = '1';";

    // $repairAttrs = $rpQuery->get_results( $sql );
    $repairAttrs = rp_get_all_attrs_of_repair_order_by_position( $repairRow->r_id );

    if( !empty( $repairAttrs ) ) {

        foreach( $repairAttrs as $key => $attr_row ) {

            if( isset( $rp_pre_selected_location ) && !empty( $rp_pre_selected_location ) ) {
                $attr_row = apply_filters('rp_overwrite_individual_repair_attr_row', $attr_row, $rp_pre_selected_location);
            }

            $repairAttrs[$key] = $attr_row;

        }

        $m_uq_id = get_m_uq_id_by_m_id( $repairRow->m_id_fk );

        $repairAttrs = rp_filter_all_repair_attr_before_return( $repairAttrs, $m_uq_id );

    } else {

        $repairAttrs = array();

    }

    return $repairAttrs;

}

function rp_find_single_attribtute_of_repair( $m_uq_id = '', $repairRow = null ) {

    $visible_attributes = rp_get_only_visible_attributes( $repairRow );

    if( is_array($visible_attributes) && count( $visible_attributes ) === 1 ) {

        return $visible_attributes[0];

    }

    return NULL;

}

function rp_dp_get_dynamic_price( $m_uq_id = '', $repairRow = null ) {

    $must_use_por = FALSE;

    $main_repair_uqid = '';
    
    if( property_exists( $repairRow, 'r_price' ) ) {

        $default = $repairRow->r_price;
        $margin = $repairRow->r_margin;
        $uqid = $repairRow->r_uq_id;

        if( $repairRow->is_free ) {
            return array('free', 0, array());
        }

        // if( rp_repair_has_any_attributes( $repairRow->r_id ) ) {

        //     if( !rp_is_enabled_dynamic_pricing() ) {
        //         return array('price_on_request', 0, array());
        //     }

        //     $must_use_por = TRUE;

        // }

    } else if( property_exists( $repairRow, 'a_price' ) ) {

        $default = $repairRow->a_price;
        $margin = $repairRow->a_margin;
        $uqid = $repairRow->a_uq_id;

        $relatedRepairRow = rp_find_repair_row_by_id( $repairRow->r_id_fk );

        global $rp_pre_selected_location;

        if( !empty( $relatedRepairRow ) ) {

            $main_repair_uqid = $relatedRepairRow->r_uq_id;

            if( isset( $rp_pre_selected_location ) && !empty( $rp_pre_selected_location ) ) {
                $relatedRepairRow = apply_filters('rp_overwrite_individual_repair_row', $relatedRepairRow, $rp_pre_selected_location);
            }
        }

        if( !empty( $relatedRepairRow ) && $relatedRepairRow->is_free ) {
            return array('free', 0, array());
        }
        
    } else {

        // unexpected
        return array('price_on_request', 0, array());

    }

    if( !rp_is_enabled_dynamic_pricing() ) {
        return array('fallback', $default, array());
    }

    // $rp_dp_default_margin_setting = (float) rp_get_option('rp_dp_default_margin_setting', 0);

    // temporary commented to remove this functionality
    $rp_dp_default_margin_setting = 0;

    if( $margin == 0 && $rp_dp_default_margin_setting > 0 ) {

        $margin = $rp_dp_default_margin_setting;

    }

    if( $margin == 0 || empty( $skus = rp_dp_get_skus( $m_uq_id, $uqid, $main_repair_uqid ) ) ) {

        $rp_dp_if_part_is_unavailable = rp_get_option('rp_dp_if_part_is_unavailable', 'use_fallback');

        if( $rp_dp_if_part_is_unavailable === 'use_price_on_request' || $must_use_por === TRUE ) {

            return array('price_on_request', 0, array());

        } else {

            return array('fallback', $default, array());

        }

    }

    $rp_dp_which_price_to_consider = rp_get_option('rp_dp_which_price_to_consider', 'cheapest');

    if( $rp_dp_which_price_to_consider === 'most_expensive' ) {

        $sku_row = rp_find_highest_price_sku_row( $skus );

    } else if( $rp_dp_which_price_to_consider === 'average' ) {

        $sku_row = rp_find_average_price_sku_row( $skus );

    } else {

        $sku_row = rp_find_lowest_price_sku_row( $skus );

    }

    $taxRate = (float) (rp_dp_get_tax_rate() ?? 0);
    
    $stockPrice = $sku_row->processed_price;
    $stockPrice = round($stockPrice, 2);
    if( $taxRate != 0 ) {
        $taxPercent = $taxRate / 100;
    } else {
        $taxPercent = 0;
    }

    //rp_dp_margin_type
    //rp_parse_dp_margin_setting
    $margin_type = rp_get_option('rp_dp_margin_type', 'fixed');

    $margin_percentage = 0;

    $margin_minimum = 0;

    $margin_maximum = 0;

    if( $margin_type == 'percentage' ) {

        $margin_percentage = $margin;

        $margin_minimum = rp_parse_dp_margin_setting( rp_get_option('rp_dp_margin_minimum', '') ?? 0 );

        $margin_maximum = rp_parse_dp_margin_setting( rp_get_option('rp_dp_margin_maximum', '') ?? 0 );

        $margin = round( $margin * ( $stockPrice / 100 ), 2 );

        if( $margin_minimum !== 0 && $margin_minimum > $margin ) {

            $margin = $margin_minimum;

        }

        if( $margin_maximum !== 0 && $margin_maximum < $margin ) {

            $margin = $margin_maximum;

        }
        
    }

    $finalPriceBeforeRoundUp = $stockPrice + $margin; // Add margin to the price
    $taxPrice = round( ($finalPriceBeforeRoundUp * $taxPercent), 2 );
    $finalPriceBeforeRoundUp = round($finalPriceBeforeRoundUp + $taxPrice, 2); // Add tax to the price

    $rp_dp_round_up_dynamic_price = rp_get_option('rp_dp_round_up_dynamic_price', '1');
    $expected_values = array('ceil', '0', '95', '99', '100');
    $rp_dp_round_up_method = rp_get_option('rp_dp_round_up_method', 'ceil');

    $rp_dp_round_up_every_x_units = rp_get_option('rp_dp_round_up_every_x_units', '5');

    if( $rp_dp_round_up_dynamic_price == '1' && in_array( $rp_dp_round_up_method, $expected_values ) ) {
        // Need to round up the price
        if( $rp_dp_round_up_every_x_units == '10' ) {
            $finalPriceAfterRoundUp = rp_round_up_nearest_ten( $finalPriceBeforeRoundUp, $rp_dp_round_up_method );
        } else {
            $finalPriceAfterRoundUp = rp_round_up_nearest_five( $finalPriceBeforeRoundUp, $rp_dp_round_up_method );
        }
    } else {
        $finalPriceAfterRoundUp = $finalPriceBeforeRoundUp;
    }

    // final tax calculation:
    $finalTax = round( ($finalPriceAfterRoundUp / ($taxRate + 100)) * $taxRate, 2 );
    $finalExcludingTax = $finalPriceAfterRoundUp - $finalTax;

    return array('dynamic', $finalPriceAfterRoundUp, array(
        'all_skus' => $skus,
        'sku_row' => $sku_row,
        'stock_price' => $stockPrice,
        'margin' => $margin,
        'tax_rate' => $taxRate,
        'tax_price' => $taxPrice,
        'final_tax' => $finalTax,
        'final_excluding_tax' => $finalExcludingTax,
        'before_round_up' => $finalPriceBeforeRoundUp,
        'after_round_up' => $finalPriceAfterRoundUp,
        'rp_dp_round_up_dynamic_price' => $rp_dp_round_up_dynamic_price,
        'rp_dp_round_up_method' => $rp_dp_round_up_method,
        'rp_dp_round_up_every_x_units' => $rp_dp_round_up_every_x_units,
        'rp_dp_which_price_to_consider' => $rp_dp_which_price_to_consider,
        'calculated_at_time' => time(),
        'margin_type' => $margin_type,
        'margin_percentage' => $margin_percentage,
        'margin_minimum' => $margin_minimum,
        'margin_maximum' => $margin_maximum
    ));

}

function rp_filter_repair_before_return_edit_appointment( $repairRow = null, $m_uq_id = '' ) {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return $repairRow;

    }

    list( $method, $price, $debug ) = rp_dp_get_dynamic_price( $m_uq_id, $repairRow );

    $repairRow->r_price = $price . '';

    $repairRow->pricing_method = $method;

    $repairRow->debug = $debug;

    return $repairRow;

}

function rp_filter_repair_attr_before_return_edit_appointment( $attrRow = null, $m_uq_id = '' ) {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return $attrRow;

    }

    list( $method, $price, $debug ) = rp_dp_get_dynamic_price( $m_uq_id, $attrRow );

    $attrRow->a_price = $price . '';

    $attrRow->pricing_method = $method;

    $attrRow->debug = $debug;

    return $attrRow;

}

function rp_filter_all_repairs_before_return( $all_repairs = array(), $m_uq_id = '', $repair_attrs_from_db = array() ) {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return $all_repairs;

    }

    foreach( $all_repairs as $key => $repairRow ) {

        $last_synced_date = rp_get_option('rp_dp_finished_cron_processing', '');

        // format = 24/12/22
        if( !empty( $last_synced_date ) ) {

            $repairRow->last_synced_date = date( rp_get_option('rp_dp_date_format_front_end', 'd/m/y'), $last_synced_date);

        }

        if( !isset( $repair_attrs_from_db[ $repairRow->r_id ] ) ) {

            // Only do when no repair attr is found in db

            list( $method, $price, $debug ) = rp_dp_get_dynamic_price( $m_uq_id, $repairRow );

            $repairRow->r_price = $price . '';

            if( $method === 'dynamic' && !empty( $last_synced_date ) ) {

                $repairRow->is_dynamic = '1';

            } else {

                $repairRow->is_dynamic = '0';

            }

            $all_repairs[$key] = $repairRow;

        } else {

            $repairRow->is_dynamic = '0';

            foreach( $repair_attrs_from_db[ $repairRow->r_id ] as $attrKey => $attrRow ) {

                list( $method, $price, $debug ) = rp_dp_get_dynamic_price( $m_uq_id, $attrRow );

                if( $method === 'dynamic' && !empty( $last_synced_date ) ) {

                    $repairRow->is_dynamic = '1';

                }

            }

            // attr found so make the price 0
            $repairRow->r_price = '0';

            $all_repairs[$key] = $repairRow;

        }

    }

    return $all_repairs;

}

add_filter('rp_filter_all_repairs_before_return', 'rp_filter_all_repairs_before_return', 10, 3); // [R1]

function rp_filter_all_repairs_before_return_extended( $all_repairs = array(), $m_uq_id = '', $repair_attrs_from_db = array() ) {

    $rp_hide_attribute_if_only_one = rp_get_option('rp_hide_attribute_if_only_one', '1');

    if( $rp_hide_attribute_if_only_one != '1' || empty( $all_repairs ) ) {

        return $all_repairs;

    }

    foreach( $all_repairs as $key => $repairRow ) {

        $singleAttribute = rp_find_single_attribtute_of_repair( $m_uq_id, $repairRow );

        if( !empty( $singleAttribute ) ) {

            $repairRow->r_price = $singleAttribute->a_price;

            $repairRow->is_dynamic = $singleAttribute->is_dynamic ?? '0';

            $repairRow->r_readmore = rp_get_static_translations_of_attr_individually_editable('attr_read_more', $singleAttribute->a_uq_id, $singleAttribute->r_id_fk, 'selected');

            $repairRow->r_readmoretext = rp_get_static_translations_of_attr_individually_editable('attr_readmore_text', $singleAttribute->a_uq_id, $singleAttribute->r_id_fk, 'selected');

        }

        $all_repairs[$key] = $repairRow;

    }

    return $all_repairs;

}

add_filter('rp_filter_all_repairs_before_return', 'rp_filter_all_repairs_before_return_extended', 11, 3);


function rp_filter_all_repair_attr_before_return( $repair_attr = array(), $m_uq_id = '' ) {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return $repair_attr;

    }

    $rp_hide_attribute_if_no_part = rp_get_option('rp_hide_attribute_if_no_part', '0');

    foreach( $repair_attr as $key => $attrRow ) {

        list( $method, $price, $debug ) = rp_dp_get_dynamic_price( $m_uq_id, $attrRow );

        if( $rp_hide_attribute_if_no_part === '1' && ($price == 0 || $price == 0.00 || empty($price)) ) {
                
            unset( $repair_attr[$key] );

            continue;

        } else {

            $attrRow->a_price = $price . '';

            $attrRow->is_dynamic = $method === 'dynamic' ? '1' : '0';

            $repair_attr[$key] = $attrRow;

        }

    }

    $reindex = array();

    foreach( $repair_attr as $key => $attrRow ) {

        $reindex[] = $attrRow;

    }

    return $reindex;

}

add_filter('rp_filter_all_repair_attr_before_return', 'rp_filter_all_repair_attr_before_return', 10, 2); // [A1]

function rp_hide_attribute_if_only_one( $repair_attr = array(), $m_uq_id = '' ) {

    // Show repair attribute information if there is only one attribute

    $rp_hide_attribute_if_only_one = rp_get_option('rp_hide_attribute_if_only_one', '1');

    if( $rp_hide_attribute_if_only_one === '1' && count( $repair_attr ) === 1 ) {

        return array();

    }

    return $repair_attr;

}

add_filter('rp_filter_all_repair_attr_before_return', 'rp_hide_attribute_if_only_one', 11, 2);

function get_m_uq_id_by_m_id( $m_id = 0 ) {

    global $rpQuery;

    static $cache = array();

    $m_id = intval( $m_id );

    if( isset( $cache[ $m_id ] ) ) {

        return $cache[ $m_id ];

    }

    $rs_model = $rpQuery->prefix . 'rs_model';

    $sql = "SELECT * FROM `$rs_model` WHERE m_id = $m_id";

    $modelRow = $rpQuery->get_row( $sql );

    if( !empty( $modelRow ) ) {

        $cache[ $m_id ] = $modelRow->m_uq_id;

        return $cache[ $m_id ];

    }

    return '';

}

function rp_filter_before_adding_repair_to_cart( $row = null ) {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return $row;

    }

    $m_uq_id = get_m_uq_id_by_m_id($row->m_id_fk);

    if(empty($m_uq_id)) {

        return $row;

    }

    list( $method, $price, $debug ) = rp_dp_get_dynamic_price( $m_uq_id, $row );

    $row->r_price = $price . '';

    return $row;

}

add_filter('rp_filter_before_adding_repair_to_cart', 'rp_filter_before_adding_repair_to_cart', 10, 1); // [R2]

function rp_filter_before_adding_repair_to_cart_extended( $row = null ) {

    $rp_hide_attribute_if_only_one = rp_get_option('rp_hide_attribute_if_only_one', '1');

    if( $rp_hide_attribute_if_only_one != '1' || empty( $row ) ) {

        return $row;

    }

    $m_uq_id = get_m_uq_id_by_m_id($row->m_id_fk);

    if(empty($m_uq_id)) {

        return $row;

    }

    $singleAttribute = rp_find_single_attribtute_of_repair( $m_uq_id, $row );

    if( !empty( $singleAttribute ) ) {

        $row->r_price = $singleAttribute->a_price;

        $row->is_dynamic = $singleAttribute->is_dynamic ?? '0';

    }

    return $row;

}

add_filter('rp_filter_before_adding_repair_to_cart', 'rp_filter_before_adding_repair_to_cart_extended', 11, 1);

function rp_filter_before_adding_repair_attr_to_cart( $row = null, $repairRow = null ) {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return $row;

    }

    $m_uq_id = get_m_uq_id_by_m_id($repairRow->m_id_fk);

    if(empty($m_uq_id)) {

        return $row;

    }

    list( $method, $price, $debug ) = rp_dp_get_dynamic_price( $m_uq_id, $row );

    $row->a_price = $price . '';

    $row->is_dynamic = $method === 'dynamic' ? '1' : '0';

    return $row;

}

add_filter('rp_filter_before_adding_repair_attr_to_cart', 'rp_filter_before_adding_repair_attr_to_cart', 10, 2);


function rp_inserted_row_in_rs_order_repairs( $insert_id = 0, $order_id_fk = 0, $r_id = 0 ) {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return;

    }

    global $rpQuery;

    $rs_order_repairs = $rpQuery->prefix . 'rs_order_repairs';

    $related_repair = rp_get_where_query('rs_repair', 'r_id', $r_id, TRUE, '=');

    if( !empty( $related_repair ) ) {

        global $rp_pre_selected_location;

        if( isset( $rp_pre_selected_location ) && !empty( $rp_pre_selected_location ) ) {
            
            $related_repair = apply_filters('rp_overwrite_individual_repair_row', $related_repair, $rp_pre_selected_location);
                
        }

        $m_uq_id = get_m_uq_id_by_m_id($related_repair->m_id_fk);

        if(!empty($m_uq_id)) {

            list( $method, $price, $debug ) = rp_dp_get_dynamic_price( $m_uq_id, $related_repair );

            // update rs_order_repairs where or_id = $insert_id
            // set pricing_method = $method, pricing_info = serialize($debug)

            $rpQuery->query("UPDATE `$rs_order_repairs` SET pricing_method = ':pricing_method', pricing_info = ':pricing_info' WHERE or_id = ':or_id' AND o_id_fk = ':o_id_fk'", array(

                'pricing_method' => $method,

                'pricing_info' => serialize($debug),

                'or_id' => $insert_id,

                'o_id_fk' => $order_id_fk

            ));

        }

    }

}

rp_add_action('rp_inserted_row_in_rs_order_repairs', 'rp_inserted_row_in_rs_order_repairs', 10, 3);

function rp_inserted_row_in_rs_order_repairs_extended( $insert_id = 0, $order_id_fk = 0, $r_id = 0 ) {

    $rp_hide_attribute_if_only_one = rp_get_option('rp_hide_attribute_if_only_one', '1');

    if( $rp_hide_attribute_if_only_one != '1' ) {

        return;

    }

    global $rpQuery;

    $rs_order_repairs = $rpQuery->prefix . 'rs_order_repairs';

    $related_repair = rp_get_where_query('rs_repair', 'r_id', $r_id, TRUE, '=');

    if( !empty( $related_repair ) ) {

        global $rp_pre_selected_location;

        if( isset( $rp_pre_selected_location ) && !empty( $rp_pre_selected_location ) ) {
            
            $related_repair = apply_filters('rp_overwrite_individual_repair_row', $related_repair, $rp_pre_selected_location);
                
        }

        $m_uq_id = get_m_uq_id_by_m_id($related_repair->m_id_fk);

        if(!empty($m_uq_id)) {

            $visible_attributes = rp_get_only_visible_attributes( $related_repair );

            if( is_array($visible_attributes) && count( $visible_attributes ) === 1 ) {

                $attribute = $visible_attributes[0];

                rp_insert_into_rs_order_repair_attr( $order_id_fk, $attribute->a_id );

            }

        }

    }

}

rp_add_action('rp_inserted_row_in_rs_order_repairs', 'rp_inserted_row_in_rs_order_repairs_extended', 11, 3);


function rp_inserted_row_in_rs_order_repair_attr( $insert_id = 0, $order_id_fk = 0, $a_id = 0 ) {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return;

    }

    global $rpQuery;

    $rs_order_repair_attr = $rpQuery->prefix . 'rs_order_repair_attr';

    $related_repair_attr = rp_get_where_query('rs_repair_attr', 'a_id', $a_id, TRUE, '=');

    if(!empty($related_repair_attr)) {

        $repairRow = rp_find_repair_row_by_id( $related_repair_attr->r_id_fk );

        global $rp_pre_selected_location;

        if( isset( $rp_pre_selected_location ) && !empty( $rp_pre_selected_location ) ) {
                
            $repairRow = apply_filters('rp_overwrite_individual_repair_row', $repairRow, $rp_pre_selected_location);

            $related_repair_attr = apply_filters('rp_overwrite_individual_repair_attr_row', $related_repair_attr, $rp_pre_selected_location);
                
        }

        $m_uq_id = get_m_uq_id_by_m_id($repairRow->m_id_fk);

        if(!empty($m_uq_id)) {

            list( $method, $price, $debug ) = rp_dp_get_dynamic_price( $m_uq_id, $related_repair_attr );

            // update rs_order_repair_attr where ora_id = $insert_id
            // set pricing_method = $method, pricing_info = serialize($debug)

            $rpQuery->query("UPDATE `$rs_order_repair_attr` SET pricing_method = ':pricing_method', pricing_info = ':pricing_info' WHERE ora_id = ':ora_id' AND o_id_fk = ':o_id_fk'", array(

                'pricing_method' => $method,

                'pricing_info' => serialize($debug),

                'ora_id' => $insert_id,

                'o_id_fk' => $order_id_fk

            ));

        }

    }

}

rp_add_action('rp_inserted_row_in_rs_order_repair_attr', 'rp_inserted_row_in_rs_order_repair_attr', 10, 3);

function rp_dp_show_price_was_updated_on( $bool = FALSE ) {

    return TRUE;

}

rp_add_action('rp_dp_show_price_was_updated_on', 'rp_dp_show_price_was_updated_on', 10, 1);


function rp_dp_add_new_translation_by_release( $new_translation_groups = array() ) {

    $new_translation_groups['rp_dp_plugin_translation_v_1p0'] = array(

        'the_price_was_updated_on'

    );

    return $new_translation_groups;

}

add_filter('rp_force_fetch_new_translation_groups', 'rp_dp_add_new_translation_by_release', 11, 1);

function rp_get_uq_ids_of_all_available_repairs() {

    global $rpQuery;

    $rs_default_repair = $rpQuery->prefix . 'rs_default_repair';

    $rs_category = $rpQuery->prefix . 'rs_category';

    // first of all, get all rows from rs_default_repair group by dr_uq_id and dr_category

    $default_repairs = $rpQuery->get_results("SELECT $rs_default_repair.dr_uq_id, $rs_category.c_uq_id FROM `$rs_default_repair` 
        INNER JOIN `$rs_category` ON `$rs_default_repair`.dr_category = `$rs_category`.c_id 
        GROUP BY dr_uq_id, dr_category");

    $dr_uq_ids = array();

    foreach( $default_repairs as $default_repair ) {

        $dr_uq_ids[] = $default_repair->c_uq_id. '|' .  $default_repair->dr_uq_id;

    }

    if( empty( $dr_uq_ids ) ) {

        return array();

    }

    $dr_uq_ids = array_unique( $dr_uq_ids );

    return $dr_uq_ids;

}

function rp_download_attributes_of_installed_repairs() {

    static $all_attributes = NULL;

    if( $all_attributes !== NULL ) {

        return $all_attributes;

    }

    global $rpQuery;

	require_once RP_DP_PLUGIN_PATH . 'classes/Base_API.php';

	$base_api = new RepairPluginPro\Base_API($rpQuery);
	
	$response = $base_api->fetch('Endpoints/fetch_all_attributes', array(), FALSE);
	
	$all_attributes = array();
	
	if( isset( $response ) && is_object( $response ) && property_exists( $response, 'all_attributes' ) ) {
	
		$all_attributes = $response->all_attributes;
	
	}

    return $all_attributes;

}

function rp_get_cached_or_download_all_attributes() {

    $rp_dp_last_downloaded_all_attrs = rp_get_option('rp_dp_last_downloaded_all_attrs', FALSE);

    $all_attributes = FALSE;
    $last_synced = FALSE;

    if( $rp_dp_last_downloaded_all_attrs !== FALSE && is_array($rp_dp_last_downloaded_all_attrs) && isset( $rp_dp_last_downloaded_all_attrs['last_synced'] ) ) {

        if( $rp_dp_last_downloaded_all_attrs['last_synced'] > ( time() - 600 ) ) {

            // it is less than 10 minutes since the last download, use the cached version
            $all_attributes = $rp_dp_last_downloaded_all_attrs['response'];
            $last_synced = $rp_dp_last_downloaded_all_attrs['last_synced'];

        }

    }

    if( $all_attributes === FALSE ) {

        // download the all_attributes
        $all_attributes = rp_download_attributes_of_installed_repairs();

        if( !empty( $all_attributes ) ) {

            $last_synced = time();

            // save the all_attributes
            rp_update_option('rp_dp_last_downloaded_all_attrs', array(
                'last_synced' => $last_synced,
                'response' => $all_attributes
            ));

        } else {

            $all_attributes = FALSE;
            $last_synced = FALSE;

        }

    }

    if( $all_attributes === FALSE && $rp_dp_last_downloaded_all_attrs !== FALSE && is_array($rp_dp_last_downloaded_all_attrs) && isset( $rp_dp_last_downloaded_all_attrs['last_synced'] ) ) {

        // download failed
        // more than 10 minutes but we have no other option, use the cached version
        $all_attributes = $rp_dp_last_downloaded_all_attrs['response'];
        $last_synced = $rp_dp_last_downloaded_all_attrs['last_synced'];

    }

    return array($all_attributes, $last_synced);

}

function rp_find_missing_repair_attributes() {

    if( !rp_is_enabled_dynamic_pricing() ) {

        return array();

    }

    global $rpQuery;

    $rs_default_repair = $rpQuery->prefix . 'rs_default_repair';

    $rs_default_repair_attr = $rpQuery->prefix . 'rs_default_repair_attr';

    $rs_category = $rpQuery->prefix . 'rs_category';

    list( $all_attributes, $last_synced ) = rp_get_cached_or_download_all_attributes();

    $missingAttributes = array();

    $availableRepairs = rp_get_uq_ids_of_all_available_repairs();

    if( !empty( $all_attributes ) ) {

        foreach( $all_attributes as $theAttribute ) {

            list( $repair_uq_id, $c_uq_id, $da_uq_id ) = explode('|', $theAttribute);

            if( !in_array( $c_uq_id . '|' . $repair_uq_id, $availableRepairs ) ) {

                continue;

            }

            $sql = "SELECT * FROM `$rs_default_repair_attr` 
                INNER JOIN `$rs_category` ON `$rs_default_repair_attr`.dr_category = `$rs_category`.c_id 
                INNER JOIN `$rs_default_repair` ON `$rs_default_repair_attr`.dr_id_fk = `$rs_default_repair`.dr_id
                WHERE `$rs_category`.c_uq_id = ':c_uq_id' AND `$rs_default_repair_attr`.da_uq_id = ':da_uq_id'";

            $related_default_repair_attr = $rpQuery->get_row($sql, array( 'c_uq_id' => $c_uq_id, 'da_uq_id' => $da_uq_id ));

            if( empty( $related_default_repair_attr ) ) {

                $missingAttributes[] = $theAttribute;

            }
            
        }

    }

    return $missingAttributes;

}

function rp_update_dp_supplier_linking() {

    rp_verify_csrf_token_for_ajax('dynamic_pricing');

    $supplier_id = $_POST['supplier_id'] ?? '0';

    $b_ids = $_POST['b_ids'] ?? array();

    if( !empty( $supplier_id ) ) {

        rp_update_option('rp_dp_supplier_linking_' . $supplier_id, $b_ids);

    }

    echo json_encode(array('status' => true));

    exit();

}

rp_ajax_for_admin('rp_update_dp_supplier_linking', 'rp_update_dp_supplier_linking');

function rp_get_all_brand_ids_for_dp_supplier_linking() {

    global $rpQuery;

    $rs_brand = $rpQuery->prefix . 'rs_brand';

    $sql = "SELECT b_id FROM `$rs_brand`";

    $all_brands = $rpQuery->get_results($sql);

    if( empty( $all_brands ) ) {

        return array();

    }

    $all_brand_ids = array();

    foreach( $all_brands as $brand_row ) {

        $all_brand_ids[] = $brand_row->b_id;

    }

    return $all_brand_ids;

}

function rp_get_manage_dynamic_pricing_linking_markup() {

    $supplier_id = $_POST['supplier_id'] ?? '0';

    $linked_brands = rp_get_option('rp_dp_supplier_linking_' . $supplier_id, FALSE);

    if( $linked_brands === FALSE ) {

        $linked_brands = rp_get_all_brand_ids_for_dp_supplier_linking();

    }

    ob_start();

    $categories = rp_get_all_models_for_upsales_page();

    $show_update_btn = false;

    if( !empty( $categories ) ) {

        $show_update_btn = true;

        ?>
        <p style="font-family: 'Montserrat'; font-size: 13px; margin-bottom: 0px;">Choose which categories and brands to link with this supplier (<b><?php echo $_POST['supplier_name'] ?? ''; ?></b>).</p>
        <input type="hidden" name="supplier_id" value="<?php echo $supplier_id; ?>">
        <div class="details-container clearfix">
            <div class="wp-repair-col-4">
                <div class="multicheckboxes">
                    <ul id="tree">
                        <?php 
                        foreach($categories as $category_row) {
                            $category_row->c_name = rp_sanitize_output($category_row->c_name);
                            $c_checked = '';
                            // check if any brand is linked with this category
                            foreach($category_row->_brands as $brand_row) {
                                if( in_array( $brand_row->b_id, $linked_brands ) ) {
                                    $c_checked = 'checked="checked"';
                                    break;
                                }
                            }
                            ?>
                            <li>
                            <button type="button" class="btn-dropdown"><span class="chevron up"><i class="fas fa-chevron-down"></i></span></button>
                            <label class="checkbox-container">
                                <input type="checkbox" <?php echo $category_row->c_name; ?> name="c_id" value="<?php echo $category_row->c_name; ?>" <?php echo $c_checked; ?>>
                                <span class="checkmark"></span>
                                <span class="text"><?php echo $category_row->c_name; ?></span>
                            </label>
                            <ul class="opened">
                            <?php
                            foreach($category_row->_brands as $brand_row) {
                                $checked = in_array( $brand_row->b_id, $linked_brands ) ? 'checked="checked"' : '';
                                $brand_row->b_name = rp_sanitize_output($brand_row->b_name);
                                ?>
                                <li>
                                <label class="checkbox-container">
                                        <input type="checkbox" <?php echo $brand_row->b_name; ?> name="b_id" value="<?php echo $brand_row->b_id; ?>" <?php echo $checked; ?>>
                                        <span class="checkmark"></span>
                                        <span class="text"><?php echo $brand_row->b_name; ?></span>
                                </label>
                                </li>
                                <?php
                            }
                            ?>
                            </ul>
                            </li>
                            <?php
                        }
                        ?>
                    </ul>
                </div>
            </div>
        </div> 
        <?php

    } else {

        ?>
        <p>Looks like you do not have any created categories and brands, You need to create a category that contains both a brand and a model before you can manage linking.</p>
        <p>If you prefer not to do this manually, you can <a href="?page=wp_repair_download_libraries&section=download_libraries">download over 1900 models</a> from our database, and our import function will automatically create a category and brand associated with each model.</p>
        <?php
        
    }

    $html = ob_get_clean();

    echo json_encode(array('status' => true, 'html_markup' => $html, 'show_update_btn' => $show_update_btn));

    exit();
    
}

rp_ajax_for_admin('rp_get_manage_dynamic_pricing_linking_markup', 'rp_get_manage_dynamic_pricing_linking_markup');

function rp_fix_enabled_suppliers_after_dp_v2_update() {

    // make sure it only run once per setup
    if( rp_get_option('rp_fix_enabled_suppliers_after_dp_v2_update', FALSE) == '1' ) {

        return;

    }

    $dp_cron_lock = RP_DP_PLUGIN_PATH . 'classes/longProcess/lock.txt';

    if( file_exists( $dp_cron_lock ) ) {

        // Cron is in progress so deleting
        // related options isn't a solution
        // Because at the end of cron request
        // the step / page will update again
        // So we better run this code when
        // the cron is not running
        return;

    }

    rp_update_option('rp_fix_enabled_suppliers_after_dp_v2_update', '1');

    $switch_ids = array(

        // v1 ids => v2 ids
        // TouchFix
        3 => 1,

        // Fixje
        4 => 3,

        // Directscherm
        6 => 5,

        // Euro Mobile Company
        7 => 4,

        // Foneday
        8 => 2,

        // Mobileparts.shop
        9 => 6

    );

    global $rpQuery;

    $options = $rpQuery->prefix . 'options';

    $sql = "SELECT * FROM `$options` WHERE option_name LIKE 'rp_dp_supplier_linking_%'";

    $all_linkings = $rpQuery->get_results($sql);

    if( !empty( $all_linkings ) ) {

        foreach( $all_linkings as $optionRow ) {

            $supplier_id = (int) ( str_replace('rp_dp_supplier_linking_', '', $optionRow->option_name) );
    
            // delete where option id matches
            $rpQuery->query("DELETE FROM `$options` WHERE option_id = '". rp_escape_sql($optionRow->option_id) ."'");
    
            if( isset( $switch_ids[ $supplier_id ] ) ) {
    
                $switched_id = $switch_ids[ $supplier_id ];
    
                $rpQuery->query("INSERT INTO `$options` (option_name, option_value, autoload) VALUES ('rp_dp_supplier_linking_$switched_id', '". rp_escape_sql($optionRow->option_value) ."', 'yes')");
    
            }
    
        }

    }

    $enabled_suppliers = rp_get_option( 'rp_dp_enabled_suppliers', array() );

    if( !empty( $enabled_suppliers ) ) {

        $filteredSupplierIds = array();

        foreach( $enabled_suppliers as $supplierId ) {

            if( isset( $switch_ids[ $supplierId ] ) ) {

                $filteredSupplierIds[] = $switch_ids[ $supplierId ];

            }
            
        }

        rp_update_option( 'rp_dp_enabled_suppliers', $filteredSupplierIds );
        
    }

    // delete cron related options
    // So we restart the cron from scratch!
    rp_delete_option('rp_dp_long_process_step');

    rp_delete_option('rp_dp_long_process_last_time');

    rp_re_download_supplier_after_dp_v2_update();

}

add_action('init', 'rp_fix_enabled_suppliers_after_dp_v2_update', 9999);

function rp_re_download_supplier_after_dp_v2_update() {

    static $itWorked = false;

    if( $itWorked === false ) {

        $itWorked = true;

        // Forgot cached suppliers so new can be downloaded
        rp_update_option('rp_dp_last_downloaded_suppliers', FALSE);

        // Update the suppliers
        rp_get_cached_or_download_suppliers();

    }

}

function rp_reset_rs_dp_linking_table_after_dp_v2_update() {

    global $wpdb;

    $table_name = $wpdb->prefix . 'rs_dp_linking';

    $sql = "TRUNCATE TABLE {$table_name}";

    $wpdb->query( $sql );

}

function rp_reset_cron_process_after_dp_v2_update2() {

    // make sure it only run once per setup
    if( rp_get_option('rp_reset_cron_process_after_dp_v2_update2', FALSE) == '1' ) {

        return;

    }

    $dp_cron_lock = RP_DP_PLUGIN_PATH . 'classes/longProcess/lock.txt';

    if( file_exists( $dp_cron_lock ) ) {

        // Cron is in progress so deleting
        // related options isn't a solution
        // Because at the end of cron request
        // the step / page will update again
        // So we better run this code when
        // the cron is not running
        return;

    }

    rp_update_option('rp_reset_cron_process_after_dp_v2_update2', '1');

    // delete cron related options
    // So we restart the cron from scratch!
    rp_delete_option('rp_dp_long_process_step');

    rp_delete_option('rp_dp_long_process_last_time');

    rp_reset_rs_dp_linking_table_after_dp_v2_update();

    rp_re_download_supplier_after_dp_v2_update();

}

add_action('init', 'rp_reset_cron_process_after_dp_v2_update2', 9999);

function rp_dp_statistics( $atts ) {

    $atts = array_change_key_case( ( array )$atts, CASE_LOWER );

    $shortcode_attributes = shortcode_atts(

    array(

        'type' => '',

        'supplier' => ''

    ), $atts );

    // remove double quotes from type and suppliers
    $shortcode_attributes['type'] = str_replace('"', '', $shortcode_attributes['type']);

    if( !is_numeric( $shortcode_attributes['supplier'] ) ) {

        $shortcode_attributes['supplier'] = str_replace('"', '', $shortcode_attributes['supplier']);

    }

    $supplier = $shortcode_attributes['supplier'];

    $type = trim( strtolower( esc_attr( $shortcode_attributes['type'] ) ) );

    $valid_types = array('models', 'repairs', 'parts');

    if( empty( $type ) || empty( $supplier ) || !in_array( $type, $valid_types ) ) {

        return 'Please provide a valid type and supplier attribute';

    }

    if( is_string( $supplier ) && trim(strtolower(esc_attr( $supplier ))) === 'all' ) {

        $supplier = 'all';

    } else {

        try {

            $supplier = (int) $supplier;

        } catch( Exception|Error $e ) {

            $supplier = 0;

        }

    }

    list($suppliers, $last_synced) = rp_get_cached_or_download_suppliers( 600 );

    $connected_models = 0;

    $connected_parts = 0;

    $connected_repairs = 0;

    if( $supplier == 'all' ) {

        foreach( $suppliers as $theSupplierRow ) {

            $connected_models = (int) ($theSupplierRow->connected_models_all_suppliers ?? 0);

            $connected_parts += (int) ($theSupplierRow->connected_rows ?? 0);

            $connected_repairs = (int) ($theSupplierRow->connected_repairs_all_suppliers ?? 0);

        }

    } else {

        foreach( $suppliers as $theSupplierRow ) {

            if( $supplier === (int) $theSupplierRow->id ) {

                $connected_models = (int) ($theSupplierRow->connected_models ?? 0);

                $connected_parts = (int) ($theSupplierRow->connected_rows ?? 0);

                $connected_repairs = (int) ($theSupplierRow->connected_repairs ?? 0);

            }

        }

    }

    if( $type == 'models' ) {

        return $connected_models;

    } else if( $type == 'repairs' ) {

        return $connected_repairs;

    } else {

        return $connected_parts;

    }

}

add_shortcode('rp_dp_stats', 'rp_dp_statistics');

F1le Man4ger