<?php

// Exit if accessed directly.
if (!defined('ABSPATH')) {
    exit;
}

//////////////////////////////////////////////////////////////////
// REST routes to save and get settings
//////////////////////////////////////////////////////////////////

add_action('rest_api_init', 'gspbquery_register_route');
function gspbquery_register_route()
{

    register_rest_route('greenshift/v1', '/get-post-types/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_post_types',
            'permission_callback' => function (WP_REST_Request $request) {
                return '__return_true';
            }
        ]
    ]);

    register_rest_route('greenshift/v1', '/get-post-metas/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_post_metas',
            'permission_callback' => function (WP_REST_Request $request) {
                return current_user_can('editor') || current_user_can('administrator');
            },
            'args' => array(
                'post_type' => array(
                    'type' => 'string',
                    'required' => true,
                )
            ),
        ]
    ]);

    register_rest_route('greenshift/v1', '/get-taxonomies/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_taxonomies',
            'permission_callback' => function (WP_REST_Request $request) {
                return current_user_can('editor') || current_user_can('administrator');
            },
            'args' => array(
                'post_type' => array(
                    'type' => 'string',
                    'required' => true,
                )
            ),
        ]
    ]);
    register_rest_route('greenshift/v1', '/get-all-taxonomies/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_all_taxonomies',
            'permission_callback' => function (WP_REST_Request $request) {
                return current_user_can('editor') || current_user_can('administrator');
            }
        ]
    ]);

    register_rest_route('greenshift/v1', '/get-terms/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_terms',
            'permission_callback' => function (WP_REST_Request $request) {
                return current_user_can('editor') || current_user_can('administrator');
            },
            'args' => array(
                'taxonomy' => array(
                    'type' => 'string',
                    'required' => true,
                ),
                'show_empty' => array(
                    'type' => 'boolean',
                    'required' => true,
                ),
                'order_by' => array(
                    'type' => 'string',
                    'required' => true,
                ),
                'order' => array(
                    'type' => 'string',
                    'required' => true,
                ),
                'include' => array(
                    'type' => 'string',
                    'required' => false
                ),
                'exclude' => array(
                    'type' => 'string',
                    'required' => false
                ),
                'childof' => array(
                    'type' => 'string',
                    'required' => false
                ),
                'image_size' => array(
                    'type' => 'string',
                    'required' => false
                ),
                'hierarchy' => array(
                    'type' => 'boolean',
                    'required' => false
                ),
                'number' => array(
                    'type' => 'number',
                    'required' => false
                ),
            ),
        ]
    ]);

    register_rest_route('greenshift/v1', '/get-terms-search/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_taxonomy_terms_search',
            'permission_callback' => function (WP_REST_Request $request) {
                return current_user_can('editor') || current_user_can('administrator');
            },
            'args' => array(
                'taxonomy' => array(
                    'type' => 'string',
                    'required' => true,
                ),
                'search' => array(
                    'type' => 'string',
                    'required' => false
                ),
                'search-id' => array(
                    'type' => 'string',
                    'required' => false
                ),
            ),
        ]
    ]);

    register_rest_route('greenshift/v1', '/get-user-roles/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_user_roles',
            'permission_callback' => function (WP_REST_Request $request) {
                return current_user_can('editor') || current_user_can('administrator');
            },
            'args' => array(
                'search' => array(
                    'type' => 'string',
                    'required' => false,
                )
            ),
        ]
    ]);

    register_rest_route('greenshift/v1', '/get-post-part/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_post_parts_callback',
            'permission_callback' => '__return_true',
            'args' => array(
                'post_id' => array(
                    'type' => 'int',
                    'required' => false,
                ),
                'part' => array(
                    'type' => 'string',
                    'required' => true,
                ),
                'post_type' => array(
                    'type' => 'string',
                    'required' => true,
                ),
                'image_size' => [
                    'type' => 'string',
                    'required' => false
                ]
            ),
        ]
    ]);

    register_rest_route('greenshift/v1', '/get-post-by/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_post_by',
            'permission_callback' => function (WP_REST_Request $request) {
                return current_user_can('editor') || current_user_can('administrator');
            },
            'args' => array(
                'post_id' => array(
                    'type' => 'int',
                    'required' => false,
                ),
                'post_type' => array(
                    'type' => 'string',
                    'required' => false,
                )
            ),
        ]
    ]);

    register_rest_route('greenshift/v1', '/get-post-search/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_post_search',
            'permission_callback' => function (WP_REST_Request $request) {
                return current_user_can('editor') || current_user_can('administrator');
            },
            'args' => array(
                'search' => array(
                    'type' => 'string',
                    'required' => true,
                ),
                'post_type' => array(
                    'type' => 'string',
                    'required' => true,
                )
            ),
        ]
    ]);

    register_rest_route('greenshift/v1', '/get-image-sizes/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_image_sizes',
            'permission_callback' => function (WP_REST_Request $request) {
                return current_user_can('editor') || current_user_can('administrator');
            },
        ]
    ]);

    register_rest_route('greenshift/v1', '/api-connector/', array(
        'methods' => 'POST',
        'callback' => 'gspb_api_connector_callback',
        'permission_callback' => '__return_true'
    ));

    // regsiter route for filter block
    register_rest_route('greenshift/v1', '/get-gs-posts/', [
        'methods'  => 'POST',
        'callback' => 'gspb_filter_posts',
        'permission_callback' => '__return_true'
    ]);

    // posts indexing
    register_rest_route('greenshift/v1', '/gspb-indexing/', array(
        'methods' => 'POST',
        'callback' => 'gspb_handle_indexing_callback',
        'permission_callback' => '__return_true'
    ));

    // Fetch Count
    register_rest_route('greenshift/v1', '/indexer-count/', array(
        'methods' => 'POST',
        'callback' => 'gspb_indexer_count_callback',
        'permission_callback' => '__return_true'
    ));

    // Refresh filter chips
	register_rest_route('greenshift/v1', '/refresh-filterchips/', array(
		'methods' => 'POST',
		'callback' => 'gspb_refreshfilter_chips_callback',
		'permission_callback' => '__return_true'
	));

    register_rest_route('greenshift/v1', '/get-rating/', array(
		'methods' => 'GET',
		'callback' => 'get_wc_ratings',
		'permission_callback' => '__return_true'
	));

    register_rest_route('greenshift/v1', '/get-dynamic-part/', [
        [
            'methods' => 'GET',
            'callback' => 'gspb_get_dynamic_part_callback',
            'permission_callback' => '__return_true',
        ]
    ]);

    register_rest_route(
		'greenshift/v1',
		'/global_settings/',
		array(
			array(
				'methods'             => 'GET',
				'callback'            => 'gspb_get_global_settings',
				'permission_callback' => function () {
					return current_user_can('manage_options');
				},
				'args'                => array(),
			),
			array(
				'methods'             => 'POST',
				'callback'            => 'gspb_update_global_settings',
				'permission_callback' => function () {
					return current_user_can('manage_options');
				},
				'args'                => array(),
			),
		)
	);

	// Per-class delta endpoint. Lets the editor add/update/delete a SINGLE global
	// class without re-sending the whole global_classes array on every save, which
	// kept the POST body growing with the library size and tripped host WAF limits.
	register_rest_route(
		'greenshift/v1',
		'/global_class/',
		array(
			array(
				'methods'             => 'POST',
				'callback'            => 'gspb_update_global_class',
				'permission_callback' => function () {
					return current_user_can('manage_options');
				},
				'args'                => array(),
			),
		)
	);

	register_rest_route(
		'greenshift/v1',
		'/public_assets/',
		array(
			array(
				'methods'             => 'GET',
				'callback'            => 'gspb_get_public_assets',
				'permission_callback' => function () {
					return true;
				},
				'args'                => array(),
			),
		)
	);

	register_rest_route(
		'greenshift/v1',
		'/figma_settings/',
		array(
			array(
				'methods'             => 'GET',
				'callback'            => 'gspb_get_global_settings',
				'permission_callback' => 'greenshift_app_pass_validation',
				'args'                => array(),
			),
			array(
				'methods'             => 'GET',
				'callback'            => 'gspb_get_license_settings',
				'permission_callback' => 'greenshift_app_pass_validation',
				'args'                => array(),
			),
			array(
				'methods'             => 'POST',
				'callback'            => 'gspb_update_global_settings',
				'permission_callback' => 'greenshift_app_pass_validation',
				'args'                => array(),
			),
		)
	);

	register_rest_route(
		'greenshift/v1',
		'/license_settings/',
		array(
			array(
				'methods'             => 'GET',
				'callback'            => 'gspb_get_license_settings',
				'permission_callback' => 'greenshift_app_pass_validation',
				'args'                => array(),
			),
		)
	);

	register_rest_route(
		'greenshift/v1',
		'/css_settings/',
		array(
			array(
				'methods'             => 'POST',
				'callback'            => 'gspb_update_css_settings',
				'permission_callback' => function () {
					return current_user_can('edit_posts');
				},
				'args'                => array(),
			),
		)
	);

	register_rest_route(
		'greenshift/v1',
		'/global_wp_settings/',
		array(
			array(
				'methods'             => 'POST',
				'callback'            => 'gspb_update_global_wp_settings',
				'permission_callback' => function () {
					return current_user_can('manage_options');
				},
				'args'                => array(),
			),
		)
	);

	register_rest_route('greenshift/v1', '/convert-svgstring-from-svg-image/', [
		[
			'methods' => 'GET',
			'callback' => 'gspb_convert_svgstring_from_svg_image',
			'permission_callback' => function (WP_REST_Request $request) {
				return current_user_can('edit_posts');
			},
			'args' => array(
				'imageid' => array(
					'type' => 'string',
					'required' => true,
				)
			),
		]
	]);

	register_rest_route('greenshift/v1', '/get-csv-to-json/', [
		[
			'methods' => 'GET',
			'callback' => 'gspb_get_csv_to_json',
			'permission_callback' => function () {
				return true;
			},
			'args' => array(
				'url' => array(
					'type' => 'string',
					'required' => true,
				),
				'type' => array(
					'type' => 'string',
					'required' => false,
					'default' => 'row',
				),
				'remove_rows' => array(
					'type' => 'string',
					'required' => false,
				),
				'remove_columns' => array(
					'type' => 'string',
					'required' => false,
				),
			),
		]
	]);

	register_rest_route('greenshift/v1', '/proxy-api/', [
		[
			'methods' => 'POST',
			'callback' => 'gspb_make_proxy_api_request',
			'permission_callback' => '__return_true',
			'args' => array(
				'type' => array(
					'type' => 'string',
					'required' => true,
				),
			),
		]
	]);

	register_rest_route('greenshift/v1', '/update-custom-js', [
        'methods' => 'POST',
        'callback' => function(WP_REST_Request $request) {
            $data = $request->get_params();
			if(!empty($data) && is_array($data) && !empty($data['js'])){
				$js = get_option('gspb_block_js');
				if(!empty($js) && is_array($js)){
					foreach($data['js'] as $item){
						foreach($item as $key=>$value){
							if(!empty($value)){
								$js[$key] = $value;
							} else {
								unset($js[$key]);
							}
						}
					}
					update_option('gspb_block_js', $js);
				} else {
					$js = [];
					foreach($data['js'] as $item){
						foreach($item as $key=>$value){
							if(!empty($value)){
								$js[$key] = $value;
							}
						}
					}
					update_option('gspb_block_js', $js);
				}
				return rest_ensure_response(['success' => true, 'message' => 'Custom JS updated!']);
			}else{
				return rest_ensure_response(['success' => false, 'message' => 'No data to update']);
			}
        },
        'permission_callback' => function () {
            return current_user_can('manage_options');
        }
    ]);

    // get map pins data
    register_rest_route('greenshift/v1', '/map-pins-on-load/', array(
        'methods'             => 'POST',
        'callback'            => 'gspb_map_pins_onload_callback',
        'permission_callback' => '__return_true',
    ));

    //map refreseher
    register_rest_route('greenshift/v1', '/map-filter-refresher/', array(
        'methods'             => 'POST',
        'callback'            => 'gspb_map_filter_refresher_callback',
        'permission_callback' => '__return_true',
    ));

    //map pin click handler
    register_rest_route('greenshift/v1', '/map-pin-click/', array(
        'methods'             => 'POST',
        'callback'            => 'gspb_map_pin_click_callback',
        'permission_callback' => '__return_true',
    ));
}

//////////////////////////////////////////////////////////////////
// Get custom value shortcode
//////////////////////////////////////////////////////////////////

function gspb_data_post_processor($data, $postprocessor, $post_id, $replaceLabel, $icon){
    if ($postprocessor == 'textformat') {
        $data= wpautop(wptexturize($data));
    } else if ($postprocessor == 'ymd') {
        $date = DateTime::createFromFormat('Ymd', $data);
        $data= $date ? wp_date(get_option('date_format'), $date->format('U')) : $data;
    } else if ($postprocessor == 'ytmd') {
        $date = DateTime::createFromFormat('Y-m-d', $data);
        $data= $date ? wp_date(get_option('date_format'), $date->format('U')) : $data;
    } else if ($postprocessor == 'unixtowp') {
        $date = DateTime::createFromFormat('U', $data);
        $data= $date ? wp_date(get_option('date_format'), $date->format('U')) : $data;
    } else if ($postprocessor == 'ymdhis') {
        $date = DateTime::createFromFormat('Y-m-d H:i:s', $data);
        $data= $date ? wp_date(get_option('date_format'), $date->format('U')) : $data;
    } else if ($postprocessor == 'ymdtodiff') {
        $date = strtotime($data);
        $data= $date ? human_time_diff($date, current_time('timestamp')) : $data;
    } else if ($postprocessor == 'numberformat') {
        if (is_numeric($data)) {
            $data = number_format($data);
        }
        $data= $data;
    }else if ($postprocessor == 'numberformatenglish') {
        if (is_numeric($data)) {
            $data = number_format($data, 0, '.', ',');
        }
        $data= $data;
    }else if ($postprocessor == 'numeric') {
        $data = preg_replace('/[^0-9]/', '', $data);
    } else if ($postprocessor == 'mailto') {
        $mail = $data;
        if ($replaceLabel) {
            $mail = $replaceLabel;
        }
        $data= '<a href="mailto:' . $data . '">' . $mail . '</a>';
    } else if ($postprocessor == 'tel') {
        $tel = $data;
        if ($replaceLabel) {
            $tel = $replaceLabel;
        }
        $data= '<a href="tel:' . $data . '">' . $tel . '</a>';
    } else if ($postprocessor == 'postlink') {
        $postshow = $data;
        if ($replaceLabel) {
            $postshow = $replaceLabel;
        }
        $data= '<a href="' . get_permalink($post_id) . '">' . $postshow . '</a>';
    } else if ($postprocessor == 'extlink') {
        $link = $data;
        if ($replaceLabel) {
            $link = $replaceLabel;
        }
        $data= '<a href="' . esc_url($data) . '" rel="nofollow">' . $link . '</a>';
    }else if ($postprocessor == 'idtofile') {
        $data = wp_get_attachment_url((int)$data);
        $label = $data;
        if ($replaceLabel) {
            $label = $replaceLabel;
        }
        if ($data) {
            $data= '<a href="' . esc_url($data) . '">' . $label . '</a>';
        }
    }else if ($postprocessor == 'idtofileurl') {
        $data = wp_get_attachment_url((int)$data);
    }else if ($postprocessor == 'idtoimage') {
        $data = wp_get_attachment_image((int)$data, 'full');
    }else if ($postprocessor == 'idtoimageurl') {
        $data = wp_get_attachment_image_url((int)$data, 'full');
    }  else if ($postprocessor == 'commatolist') {
    }else if ($postprocessor == 'idtoimageurlthumb') {
        $data = wp_get_attachment_image_url((int)$data, 'thumbnail');
    }else if ($postprocessor == 'idtoimageurlthumbwoo') {
        $data = wp_get_attachment_image_url((int)$data, 'woocommerce_thumbnail');
    }   else if ($postprocessor == 'commatolist') {
        if (strpos($data, ', ') !== 'false') {
            $data = explode(', ', $data);
        } else if (strpos($data, ',') !== 'false') {
            $data = explode(',', $data);
        }
        if ($icon) {
            $icon = '<span class="gspb_meta_prefix_icon">' . greenshift_render_icon_module($icon) . '</span>';
        } else {
            $icon = '';
        }
        $data= '<ul>
<li>' . $icon . implode('</li>
<li>' . $icon, $data) . '</li>
</ul>';
    } else if ($postprocessor == 'commatotags') {
        if (strpos($data, ', ') !== 'false') {
            $data = explode(', ', $data);
        } else if (strpos($data, ',') !== 'false') {
            $data = explode(',', $data);
        } else {
            $data = [$data];
        }
        if ($icon) {
            $icon = '<span class="gspb_meta_prefix_icon">' . greenshift_render_icon_module($icon) . '</span>';
        } else {
            $icon = '';
        }
        $data= '<span class="gspb_meta_result_tags"><span>' . $icon . implode('</span><span>' . $icon, $data) .
            '</span></span>';
    } else if ($postprocessor == 'json') {
        $data = json_encode($data);
    }
    return $data;
}

function gspb_get_image_sizes()
{
    $res = [];
    foreach (wp_get_registered_image_subsizes() as $key => $val) {
        $res[] = ['label' => $key, 'value' => $key];
    }
    $res[] = ['label' => 'full', 'value' => 'full'];
    return $res;
}

function gspb_get_post_search(WP_REST_Request $request)
{
    $search = sanitize_text_field($request->get_param('search'));
    $post_type = sanitize_text_field($request->get_param('post_type'));

    $args = array(
        'post_type' => $post_type,
        's' => $search
    );

    $query = new WP_Query($args);

    $res = [];
    foreach ($query->posts as $_post) {
        $res[] = ['id' => $_post->ID, 'title' => ['rendered' => $_post->post_title]];
    }
    return $res;
}

function gspb_get_post_by(WP_REST_Request $request)
{
    $postId = intval($request->get_param('post_id'));
    $post_type = sanitize_text_field($request->get_param('post_type'));
    $_post = gspb_get_post_object_by_id($postId, $post_type);
    return [['id' => $_post->ID, 'title' => ['rendered' => $_post->post_title]]];
}

function gspb_get_post_parts_callback(WP_REST_Request $request)
{
    $postId = intval($request->get_param('post_id'));
    $part = sanitize_text_field($request->get_param('part'));
    $post_type = sanitize_text_field($request->get_param('post_type'));
    $image_size = sanitize_text_field($request->get_param('image_size'));
    $additional = sanitize_text_field($request->get_param('additional'));
    $additional2 = sanitize_text_field($request->get_param('additional2'));
    $field = sanitize_text_field($request->get_param('field'));

    if ($postId == 0 && $post_type && $part != 'site_data' && $part != 'acfsiteoption') {
        if ($part == 'taxonomy_field_get_text' || $part == 'taxonomy_field_get_image') {
            $taxonomies = get_terms(array('taxonomy' => $post_type, 'hide_empty' => false, 'number' => 1));
            if (is_wp_error($taxonomies) || empty($taxonomies)) {
                return new WP_Error('invalid_taxonomy', 'Invalid or empty taxonomy.');
            }
            $postId = $taxonomies[0]->term_id;
        } else {
            $args = array(
                'post_type' => $post_type,
                'posts_per_page' => 1,
                'post_status' => 'publish',
                'ignore_sticky_posts' => true,
                'orderby' => 'date',
                'order' => 'DESC',
                'no_found_rows' => true,
            );
            $latest_cpt = get_posts($args);
            if (!empty($latest_cpt)) {
                $postId = $latest_cpt[0]->ID;
            }
        }
    }

    $result = '';
    switch ($part) {
        case 'title':
            $result = !empty(get_the_title($postId)) ? get_the_title($postId) : __(
                'Post don\'t has a title.',
                'gl-page-builder'
            );
            break;
        case 'featured_image':
            $result = get_the_post_thumbnail($postId, $image_size);
            $result = !empty($result) ? $result : '<svg width="500" height="500" class="gspb_svg_placeholder"
            viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
            <rect width="500" height="500" fill="transparent" />
            <g clip-path="url(#clip0_1521_865)">
                <path d="M196 262.573L226.452 231.96L285.38 291" stroke="black" />
                <path
                    d="M271.8 230.867C271.8 236.63 267.142 241.3 261.4 241.3C255.658 241.3 251 236.63 251 230.867C251 225.103 255.658 220.433 261.4 220.433C267.142 220.433 271.8 225.103 271.8 230.867Z"
                    stroke="black" />
                <path d="M259.22 264.76L282.11 241.8L305 265.853" stroke="black" />
            </g>
            <rect x="196.5" y="209.5" width="108" height="81" stroke="black" />
            <defs>
                <clipPath id="clip0_1521_865">
                    <rect x="196" y="209" width="109" height="82" fill="white" />
                </clipPath>
            </defs>
        </svg>';
            break;
        case 'meta_field_image':
            $result = GSPB_get_custom_field_value($postId, $field, 'no');
            if (is_numeric($result)) {
                $result = wp_get_attachment_image($result, $image_size);
                return json_encode($result);
            }
            $result = !empty($result) ? '<img src="' . esc_url($result) . '" />' : '<svg width="500" height="500"
            class="gspb_svg_placeholder" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
            <rect width="500" height="500" fill="transparent" />
            <g clip-path="url(#clip0_1521_865)">
                <path d="M196 262.573L226.452 231.96L285.38 291" stroke="black" />
                <path
                    d="M271.8 230.867C271.8 236.63 267.142 241.3 261.4 241.3C255.658 241.3 251 236.63 251 230.867C251 225.103 255.658 220.433 261.4 220.433C267.142 220.433 271.8 225.103 271.8 230.867Z"
                    stroke="black" />
                <path d="M259.22 264.76L282.11 241.8L305 265.853" stroke="black" />
            </g>
            <rect x="196.5" y="209.5" width="108" height="81" stroke="black" />
            <defs>
                <clipPath id="clip0_1521_865">
                    <rect x="196" y="209" width="109" height="82" fill="white" />
                </clipPath>
            </defs>
        </svg>';
            break;
        case 'meta_field_image_by_id':
            if (is_numeric($postId)) {
                $result = wp_get_attachment_image($postId, $image_size);
                return json_encode($result);
            }
            break;
        case 'featured_image_get':
            $result = get_the_post_thumbnail_url($postId, $image_size);
            $result = !empty($result) ? $result : '';
            break;
        case 'meta_field_gallery':
            if ($additional == 'array_ids') {
                $result = [];
                $galleryIdArray = get_post_meta($postId, $field, true);
                if (!is_array($galleryIdArray) && strpos($galleryIdArray, ',') !== false) $galleryIdArray =
                    wp_parse_list($galleryIdArray);
                if (!is_array($galleryIdArray)) $galleryIdArray = get_post_meta($postId, $field, false);
                if (!empty($galleryIdArray)) {
                    foreach ($galleryIdArray as $image_id) {
                        $imgsrc = wp_get_attachment_url($image_id);
                        $result[] = $imgsrc;
                    }
                }
            } else if ($additional == 'array_urls') {
                $galleryData = get_post_meta($postId, $field, true);
                if (is_array($galleryData) && !empty($galleryData)) {
                    $result = $galleryData;
                } else if (!is_array($galleryData) && !empty($galleryData)) {
                    $result = wp_parse_list($galleryData);
                }
            } else if ($additional == 'acf_gallery') {
                if (function_exists('get_field')) {
                    $result = [];
                    $galleryIdArray = get_field($field, $postId);
                    if (!empty($galleryIdArray)) {
                        foreach ($galleryIdArray as $image_id) {
                            if (is_array($image_id)) {
                                $imgsrc = $image_id['url'];
                            } else if (is_numeric($image_id)) {
                                $imgsrc = wp_get_attachment_url($image_id);
                            } else {
                                $imgsrc = $image_id;
                            }
                            $result[] = $imgsrc;
                        }
                    }
                }
            } else if ($additional == 'post_images') {
                $result = [];
                $galleryIdArray = get_attached_media('image', $postId);
                if (!empty($galleryIdArray)) {
                    foreach ($galleryIdArray as $image_id) {
                        $result[] = $image_id['guid'];
                    }
                }
            }
            if ($additional2 === 'true') {
                $im = get_the_post_thumbnail_url($postId, 'full');
                if ($im) {
                    array_unshift($result, get_the_post_thumbnail_url($postId, 'full'));
                }
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'meta_field_gallery_repeater':
            $result = [];
            $galleryIdArray = $field;
            if (!empty($galleryIdArray)) {
                if (!is_array($galleryIdArray)) {
                    $galleryIdArray = wp_parse_list($galleryIdArray);
                }
                if (is_array($galleryIdArray)) {
                    foreach ($galleryIdArray as $image_id) {
                        if (is_array($image_id)) {
                            $imgsrc = $image_id['url'];
                        } else if (is_numeric($image_id)) {
                            $imgsrc = wp_get_attachment_url($image_id);
                        } else {
                            $imgsrc = $image_id;
                        }
                        $result[] = $imgsrc;
                    }
                }
            }
            if ($additional2 === 'true') {
                $im = get_the_post_thumbnail_url($postId, 'full');
                if ($im) {
                    array_unshift($result, get_the_post_thumbnail_url($postId, 'full'));
                }
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'meta_repeater':
            $result = [];
            if ($additional == 'ase' && function_exists('get_cf')) {
                $getrepeatable = get_cf($field, 'raw', $postId);
            }
            else if ($additional == 'acf' && function_exists('get_field')) {
                $getrepeatable = get_field($field, $postId);
            } else if ($additional == 'relationpostobj' || $additional == 'relationpostids') {
                if (function_exists('get_field')) {
                    $getrepeatable = get_field($field, $postId);
                } else {
                    $getrepeatable = GSPB_get_custom_field_value($postId, $field, 'flatarray');
                }
                if ($additional == 'relationpostids' || !is_array($getrepeatable)) {
                    if (!empty($getrepeatable) && !is_array($getrepeatable)) {
                        $ids = wp_parse_id_list($getrepeatable);
                    } else {
                        $ids = $getrepeatable;
                    }
                    if (!empty($ids)) {
                        $args = array(
                            'post__in' => $ids,
                            'numberposts' => '-1',
                            'orderby' => 'post__in',
                            'ignore_sticky_posts' => 1,
                            'post_type' => 'any',
                        );
                        $getrepeatable = get_posts($args);
                    }
                }
                if (!empty($getrepeatable)) {
                    if (!is_array($getrepeatable)) {
                        $getrepeatable = [$getrepeatable];
                    }
                    $posts = [];
                    foreach ($getrepeatable as $key => $value) {
                        if (is_object($value) && !empty($value->ID)) {
                            $posts[$key] = (array) $value;
                            $posts[$key]['thumbnail_url'] = get_the_post_thumbnail_url($value->ID, 'full');
                            $posts[$key]['author'] = get_the_author_meta('display_name', $value->post_author);
                            $posts[$key]['date'] = get_the_date('', $value->ID);
                            $posts[$key]['modified_date'] = get_the_modified_date('', $value->ID);
                            $posts[$key]['permalink'] = get_the_permalink($value->ID);
                            $posts[$key]['avatar_url'] = get_avatar_url($value->post_author);

                            $remove_keys = [
                                'post_author',
                                'post_date',
                                'post_date_gmt',
                                'post_status',
                                'comment_status',
                                'ping_status',
                                'post_password',
                                'to_ping',
                                'pinged',
                                'post_modified',
                                'post_modified_gmt',
                                'post_parent',
                                'menu_order',
                                'post_type',
                                'post_mime_type',
                                'filter',
                                'guid'
                            ];
                            foreach ($remove_keys as $keyname) {
                                unset($posts[$key][$keyname]);
                            }
                            $custom_fields = get_post_meta($value->ID);
                            foreach ($custom_fields as $fieldindex => $fieldvalue) {
                                if (is_serialized($fieldvalue[0])) {
                                    $fieldvalue[0] = maybe_unserialize($fieldvalue[0]);
                                }
                                if (!empty($fieldvalue[0])) {
                                    $posts[$key][$fieldindex] = GSPB_field_array_to_value($fieldvalue[0], ', ');
                                }
                            }
                        }
                    }
                    $getrepeatable = $posts;
                }
            } else if ($additional == 'taxonomy') {
                $filters = json_decode($field, true);
                $taxonomy = !empty($filters['taxonomy']) ? esc_attr($filters['taxonomy']) : 'category';
                if (!empty($filters['show_current'])) {
                    if (is_tax() || is_category() || is_tag()) {
                        if (!empty($filters['get_current_data']) && $filters['get_current_data'] == 'term') {
                            $term = get_queried_object();
                            $getrepeatable = get_term($term->term_id, $taxonomy);
                        } else if(!empty($filters['get_current_data']) && $filters['get_current_data'] == 'loop'){
                            $getrepeatable = get_the_terms($postId, $taxonomy);
                        } else {
                            $term = get_queried_object();
                            $args = array(
                                'parent' => $term->term_id
                            );
                            if (!empty($filters['number'])) {
                                $args['number'] = (int)$filters['number'];
                            } else {
                                $args['number'] = 12;
                            }
                            if (!empty($filters['orderby'])) {
                                $args['orderby'] = sanitize_text_field($filters['orderby']);
                            }
                            if (!empty($filters['show_empty'])) {
                                $args['hide_empty'] = false;
                            }
                            if (!empty($filters['order']) && $filters['order'] == 'DESC') {
                                $args['order'] = sanitize_text_field($filters['order']);
                            }
                            if (!empty($filters['meta_key'])) {
                                $args['meta_key'] = sanitize_text_field($filters['meta_key']);
                                if (!empty($filters['meta_value'])) {
                                    $args['meta_value'] = sanitize_text_field($filters['meta_value']);
                                }
                                if (!empty($filters['meta_compare'])) {
                                    $args['meta_compare'] = sanitize_text_field($filters['meta_compare']);
                                }
                            }
                            $getrepeatable = get_terms($taxonomy, $args);
                        }
                    } else {
                        $getrepeatable = get_the_terms($postId, $taxonomy);
                    }
                } else {
                    $args = ['parent' => 0];
                    if (!empty($filters['include'])) {
                        $args['include'] = sanitize_text_field($filters['include']);
                    }
                    if (!empty($filters['exclude'])) {
                        $args['exclude'] = sanitize_text_field($filters['exclude']);
                    }
                    if (!empty($filters['orderby'])) {
                        $args['orderby'] = sanitize_text_field($filters['orderby']);
                    }
                    if (!empty($filters['show_empty'])) {
                        $args['hide_empty'] = false;
                    }
                    if (!empty($filters['order']) && $filters['order'] == 'DESC') {
                        $args['order'] = sanitize_text_field($filters['order']);
                    }
                    if (!empty($filters['meta_key'])) {
                        $args['meta_key'] = sanitize_text_field($filters['meta_key']);
                        if (!empty($filters['meta_value'])) {
                            $args['meta_value'] = sanitize_text_field($filters['meta_value']);
                        }
                        if (!empty($filters['meta_compare'])) {
                            $args['meta_compare'] = sanitize_text_field($filters['meta_compare']);
                        }
                    }
                    $getrepeatable = get_terms($taxonomy, $args);
                }
                if (!empty($getrepeatable) && !is_wp_error($getrepeatable)) {
                    if (!is_array($getrepeatable)) {
                        $getrepeatable = [$getrepeatable];
                    }
                    $terms = [];
                    $posts_by_term = [];
                    if (!empty($filters['cross_enabled'])) {
                        $cross_post_type = !empty($filters['cross_post_type']) ? $filters['cross_post_type'] : 'post';
                        $cross_limit = !empty($filters['cross_limit']) ? (int)$filters['cross_limit'] : 10;
                        $term_ids = wp_list_pluck($getrepeatable, 'term_id');
                        $args = [
                            'post_type' => $cross_post_type, // Replace with your custom post type if needed
                            'posts_per_page' => $cross_limit, // Get all posts, we'll limit them later
                            'tax_query' => [
                                [
                                    'taxonomy' => $taxonomy,
                                    'terms' => $term_ids,
                                    'field' => 'term_id',
                                ],
                            ],
                            'orderby' => 'date',
                            'order' => 'DESC',
                            'no_found_rows' => true,
                        ];

                        $posts = get_posts($args);

                        foreach ($posts as $post) {
                            $post_terms = wp_get_post_terms($post->ID, $taxonomy, ['fields' => 'ids']);

                            foreach ($post_terms as $post_term) {
                                $posts_by_term[$post_term][] = [
                                    'title' => get_the_title($post->ID),
                                    'link' => get_permalink($post->ID),
                                    'featured_image' => get_the_post_thumbnail_url($post->ID, 'full'),
                                    'author' => get_the_author_meta('display_name', $post->post_author),
                                    'date' => get_the_date('', $post->ID),
                                    'modified_date' => get_the_modified_date('', $post->ID),
                                    'permalink' => get_the_permalink($post->ID),
                                    'avatar_url' => get_avatar_url($post->post_author)
                                ];
                            }
                        }
                    }
                    foreach ($getrepeatable as $key => $value) {
                        if (is_object($value) && !empty($value->term_id)) {
                            $terms[$key] = (array) $value;
                            $terms[$key]['permalink'] = get_term_link($value);

                            $remove_keys = ['term_id', 'term_group', 'term_taxonomy_id', 'taxonomy', 'parent', 'filter'];
                            foreach ($remove_keys as $keyname) {
                                unset($terms[$key][$keyname]);
                            }
                            $custom_fields = get_term_meta($value->term_id);
                            foreach ($custom_fields as $fieldindex => $fieldvalue) {
                                if (is_serialized($fieldvalue[0])) {
                                    $fieldvalue[0] = maybe_unserialize($fieldvalue[0]);
                                }
                                if (!empty($fieldvalue[0])) {
                                    $terms[$key][$fieldindex] = GSPB_field_array_to_value($fieldvalue[0], ', ');
                                }
                                if($fieldindex == 'thumbnail_id' && !empty($fieldvalue[0])){
									$terms[$key]['thumbnail_url'] = wp_get_attachment_image_url($fieldvalue[0], 'full');
								}
                            }
                            if (!empty($posts_by_term) && !empty($posts_by_term[$value->term_id])) {
                                $terms[$key]['crossposts'] = $posts_by_term[$value->term_id];
                            }
                        }
                    }
                    $getrepeatable = $terms;
                }
            } else if($additional == 'taxonomymeta'){
                $filters = json_decode($field, true);
				$taxonomy = !empty($filters['taxonomy']) ? $filters['taxonomy'] : 'category';
				$dynamicField = !empty($filters['taxonomy_meta_field']) ? $filters['taxonomy_meta_field'] : '';
                $taxonomies = get_terms(array('taxonomy' => $taxonomy, 'hide_empty' => false, 'number' => 1));
                if(!empty($taxonomies) && is_array($taxonomies)){
                    $termId = $taxonomies[0]->term_id;
                }else{
                    $termId = 0;
                }
				if($termId){
					$getrepeatable = GSPB_get_custom_field_value($termId, $dynamicField, 'flatarray', 'taxonomymetabyid');
				}else{
					$getrepeatable = [];
				}
            } else if ($additional == 'users') {
                $filters = json_decode($field, true);
                $source = !empty($filters['source']) ? $filters['source'] : 'ids';
                $args = [];
                if ($source == 'ids') {
                    if (!empty($filters['ids'])) {
                        $ids = wp_parse_id_list($filters['ids']);
                        if (!empty($ids)) {
                            $args['include'] = $ids;
                        }
                    }
                } else if ($source == 'role') {
                    if (!empty($filters['role'])) {
                        $args['role'] = trim($filters['role']);
                    }
                } else if ($source == 'field') {
                    if (!empty($filters['field'])) {
                        $field = trim($filters['field']);
                        $field = GSPB_make_dynamic_from_metas($filters['field'], $postId);
                        $ids = wp_parse_id_list($field);
                        if (!empty($ids)) {
                            $args['include'] = $ids;
                        }
                    }
                } else if ($source == 'current') {
                    $args['include'] = get_current_user_id();
                } else if ($source == 'author') {
                    $args['include'] = get_post_field('post_author', $postId);
                }
                if (!empty($filters['orderby'])) {
                    $args['orderby'] = sanitize_text_field($filters['orderby']);
                }
                if (!empty($filters['order']) && $filters['order'] == 'DESC') {
                    $args['order'] = sanitize_text_field($filters['order']);
                }
                if (!empty($filters['meta_key'])) {
                    $args['meta_key'] = sanitize_text_field($filters['meta_key']);
                    if (!empty($filters['meta_value'])) {
                        $args['meta_value'] = sanitize_text_field($filters['meta_value']);
                    }
                    if (!empty($filters['meta_compare'])) {
                        $args['meta_compare'] = sanitize_text_field($filters['meta_compare']);
                    }
                }
                if (!empty($filters['number'])) {
                    $args['number'] = (int)$filters['number'];
                } else {
                    $args['number'] = 12;
                }
                if (!empty($args)) {
                    $users = get_users($args);
                    if (!empty($users)) {
                        $items = [];
                        $posts_by_user = [];
                        if (!empty($filters['cross_enabled'])) {
                            $cross_post_type = !empty($filters['cross_post_type']) ? $filters['cross_post_type'] : 'post';
                            $cross_limit = !empty($filters['cross_limit']) ? (int)$filters['cross_limit'] : 10;
                            $user_ids = wp_list_pluck($users, 'ID');
                            $args = [
                                'post_type' => $cross_post_type, // Replace with your custom post type if needed
                                'posts_per_page' => $cross_limit, // Get all posts, we'll limit them later
                                'author__in' => $user_ids,
                                'orderby' => 'date',
                                'order' => 'DESC',
                                'no_found_rows' => true,
                            ];

                            $posts = get_posts($args);

                            foreach ($posts as $post) {
                                $author_id = $post->post_author;
                                $posts_by_user[$author_id][] = [
                                    'title' => get_the_title($post->ID),
                                    'link' => get_permalink($post->ID),
                                    'featured_image' => get_the_post_thumbnail_url($post->ID, 'full'),
                                    'author' => get_the_author_meta('display_name', $post->post_author),
                                    'date' => get_the_date('', $post->ID),
                                    'modified_date' => get_the_modified_date('', $post->ID),
                                    'permalink' => get_the_permalink($post->ID),
                                    'avatar_url' => get_avatar_url($post->post_author)
                                ];
                            }
                        }
                        foreach ($users as $key => $value) {
                            //$items[$key] = (array) $value;
                            $items[$key]['permalink'] = get_author_posts_url($value->ID);
                            $items[$key]['avatar'] = get_avatar_url($value->ID, ['size' => !empty($filters['size']) ? (int)$filters['size']
                                : 96]);
                            $items[$key]['display_name'] = $value->display_name;
                            $items[$key]['user_email'] = $value->user_email;
                            $items[$key]['user_url'] = $value->user_url;
                            $items[$key]['user_registered'] = $value->user_registered;
                            $items[$key]['roles'] = GSPB_field_array_to_value($value->roles, ', ');
                            $items[$key]['description'] = $value->description;
                            $items[$key]['user_nicename'] = $value->user_nicename;

                            $custom_fields = get_user_meta($value->ID);
                            foreach ($custom_fields as $fieldindex => $fieldvalue) {
                                if (
                                    $fieldindex == 'rich_editing' || $fieldindex == 'syntax_highlighting' || $fieldindex == 'comment_shortcuts'
                                    || $fieldindex == 'admin_color' || $fieldindex == 'show_admin_bar_front' || $fieldindex == 'use_ssl' ||
                                    $fieldindex == 'show_welcome_panel' || $fieldindex == 'locale' || $fieldindex == 'wp_capabilities' ||
                                    $fieldindex == 'wp_user_level' || $fieldindex == 'dismissed_wp_pointers' || $fieldindex == 'session_tokens' ||
                                    $fieldindex == 'wp_dashboard_quick_press_last_post_id' || $fieldindex == 'wp_user-settings-time' || $fieldindex
                                    == 'wp_capabilities' || $fieldindex == 'wp_user-settings' || $fieldindex == 'wp_user-settings-time'
                                ) {
                                    continue;
                                }
                                if (is_serialized($fieldvalue[0])) {
                                    $fieldvalue[0] = maybe_unserialize($fieldvalue[0]);
                                }
                                if (!empty($fieldvalue[0])) {
                                    $items[$key][$fieldindex] = GSPB_field_array_to_value($fieldvalue[0], 'no');
                                }
                            }
                            if (!empty($posts_by_user) && !empty($posts_by_user[$value->ID])) {
                                $items[$key]['crossposts'] = $posts_by_user[$value->ID];
                            }
                        }
                    } else {
                        $items = [];
                    }
                } else {
                    $items = [];
                }
                $getrepeatable = $items;
            } else if ($additional == 'comments') {
                $filters = json_decode($field, true);
                $source = !empty($filters['source']) ? $filters['source'] : '';
                $args = [];
                if ($source == 'ids') {
                    if (!empty($filters['ids'])) {
                        $ids = wp_parse_id_list($filters['ids']);
                        if (!empty($ids)) {
                            $args['comment__in'] = $ids;
                        }
                    }
                } else if ($source == 'post_ids') {
                    if (!empty($filters['ids'])) {
                        $ids = wp_parse_id_list($filters['ids']);
                        if (!empty($ids)) {
                            $args['post__in'] = $ids;
                        }
                    }
                } else if ($source == 'author_ids') {
                    if (!empty($filters['ids'])) {
                        $ids = wp_parse_id_list($filters['ids']);
                        if (!empty($ids)) {
                            $args['author__in'] = $ids;
                        }
                    }
                }
                if (!empty($filters['orderby'])) {
                    $args['orderby'] = sanitize_text_field($filters['orderby']);
                }
                if (!empty($filters['order']) && $filters['order'] == 'DESC') {
                    $args['order'] = sanitize_text_field($filters['order']);
                }
                if (!empty($filters['meta_key'])) {
                    $args['meta_key'] = sanitize_text_field($filters['meta_key']);
                    if (!empty($filters['meta_value'])) {
                        $args['meta_value'] = sanitize_text_field($filters['meta_value']);
                    }
                    if (!empty($filters['meta_compare'])) {
                        $args['meta_compare'] = sanitize_text_field($filters['meta_compare']);
                    }
                }
                if (!empty($filters['number'])) {
                    $args['number'] = (int)$filters['number'];
                } else {
                    $args['number'] = 12;
                }
                if (!empty($filters['post_type'])) {
                    $args['post_type'] = sanitize_text_field($filters['post_type']);
                }
                $args['status'] = 'approve';
                $args['type'] = 'comment';
                $comments = get_comments($args);
                if (!empty($comments)) {
                    $items = [];
                    foreach ($comments as $key => $value) {
                        //$items[$key] = (array) $value;
                        $comment_id = $value->comment_ID;
                        $post_id = $value->comment_post_ID;
                        $author_id = $value->user_id; // Author ID
                        $items[$key]['permalink'] = get_comment_link($comment_id);
                        $items[$key]['content'] = $value->comment_content;
                        $items[$key]['date'] = $value->comment_date;
                        $items[$key]['date_formatted'] = date_i18n(get_option('date_format'), strtotime($value->comment_date));;
                        $items[$key]['post_permalink'] = get_the_permalink($post_id);
                        $items[$key]['post_title'] = get_the_title($post_id);

                        $items[$key]['author'] = $value->comment_author;
                        $items[$key]['author_email'] = $value->comment_author_email;
                        $items[$key]['author_url'] = $value->comment_author_url;
                        $items[$key]['author_IP'] = $value->comment_author_IP;

                        $items[$key]['avatar'] = get_avatar_url($author_id, ['size' => !empty($filters['size']) ? (int)$filters['size']
                            : 96]);

                        $custom_fields = get_comment_meta($value->comment_ID);
                        foreach ($custom_fields as $fieldindex => $fieldvalue) {
                            if (
                                $fieldindex == 'rich_editing' || $fieldindex == 'syntax_highlighting' || $fieldindex == 'comment_shortcuts'
                                || $fieldindex == 'admin_color' || $fieldindex == 'show_admin_bar_front' || $fieldindex == 'use_ssl' ||
                                $fieldindex == 'show_welcome_panel' || $fieldindex == 'locale' || $fieldindex == 'wp_capabilities' ||
                                $fieldindex == 'wp_user_level' || $fieldindex == 'dismissed_wp_pointers' || $fieldindex == 'session_tokens' ||
                                $fieldindex == 'wp_dashboard_quick_press_last_post_id' || $fieldindex == 'wp_user-settings-time' || $fieldindex
                                == 'wp_capabilities' || $fieldindex == 'wp_user-settings' || $fieldindex == 'wp_user-settings-time'
                            ) {
                                continue;
                            }
                            if (is_serialized($fieldvalue[0])) {
                                $fieldvalue[0] = maybe_unserialize($fieldvalue[0]);
                            }
                            if (!empty($fieldvalue[0])) {
                                $items[$key][$fieldindex] = GSPB_field_array_to_value($fieldvalue[0], 'no');
                            }
                        }
                    }
                } else {
                    $items = [];
                }
                $getrepeatable = $items;
            } else if ($additional == 'ce') {
                if (
                    defined('\ContentEgg\PLUGIN_PATH') &&
                    class_exists('\ContentEgg\application\components\GreenshiftIntegrator')
                ) {
                    $filters = json_decode($field, true);
                    $getrepeatable = \ContentEgg\application\components\GreenshiftIntegrator::getProductData($postId, $filters);
                    if (!empty($getrepeatable)) {
                        if (!is_array($getrepeatable)) {
                            $getrepeatable = [$getrepeatable];
                        }
                        $items = [];
                        $allowed_fields = \ContentEgg\application\components\GreenshiftIntegrator::getAllowedProductFields();
                        foreach ($getrepeatable as $key => $value) {
                            if (!empty($value) && is_array($value) && is_array($allowed_fields)) {
                                $filtered = wp_array_slice_assoc($value, $allowed_fields);
                                if (!empty($filtered)) {
                                    $items[$key] = $filtered;
                                }
                            }
                        }
                        $getrepeatable = $items;
                    }
                } else {
                    $getrepeatable = [];
                }
            } else if ($additional == 'direct') {
                $getrepeatable = $field;
            } else if ($additional == 'acpt') {
                $getrepeatable = GSPB_get_custom_field_value($postId, $field, 'flatarray');
                if (!empty($getrepeatable) && is_array($getrepeatable)) {
                    $getrepeatable = gspb_acptConvertArray($getrepeatable);
                }
            }else if ($additional == 'acptoptionrepeater') {
                $filters = json_decode($field, true);
                $option_name = !empty($filters['option_name']) ? $filters['option_name'] : '';
                $getrepeatable = get_option($option_name);
                if(!empty($getrepeatable) && is_array($getrepeatable)){
                    $getrepeatable = gspb_acptConvertArray($getrepeatable);
                }
            } else if ($additional == 'site_data') {
                $filters = json_decode($field, true);
                $option_name = !empty($filters['option_name']) ? $filters['option_name'] : '';
                $getrepeatable = get_option($option_name);
                if (!is_array($getrepeatable)) {
                    $getrepeatable = [];
                }else{
                    $items = [];
                    foreach($getrepeatable as $key => $value){
                        $items[$key] = (array) $value;
                    }
                    $getrepeatable = $items;
                }
            } else if ($additional == 'acfsiteoption' && function_exists('get_field')) {
                $filters = json_decode($field, true);
                $option_name = !empty($filters['option_name']) ? $filters['option_name'] : '';
                $getrepeatable = get_field($option_name, 'option');
                if (!is_array($getrepeatable)) {
                    $getrepeatable = [];
                }else{
                    $items = [];
                    foreach($getrepeatable as $key => $value){
                        $items[$key] = (array) $value;
                    }
                    $getrepeatable = $items;
                }
            }else if ($additional == 'aseoptionrepeater' && function_exists('get_cf')) {
                $filters = json_decode($field, true);
                $option_name = !empty($filters['option_name']) ? $filters['option_name'] : '';
                $getrepeatable = get_cf($option_name, 'raw');
                if (!is_array($getrepeatable)) {
                    $getrepeatable = [];
                }else{
                    $items = [];
                    foreach($getrepeatable as $key => $value){
                        $items[$key] = (array) $value;
                    }
                    $getrepeatable = $items;
                }
            } else if ($additional == 'transient') {
                $filters = json_decode($field, true);
                $option_name = !empty($filters['option_name']) ? $filters['option_name'] : '';
                $getrepeatable = get_transient($option_name);
                if (!is_array($getrepeatable)) {
                    $getrepeatable = [];
                }
            } else if ($additional == 'metajson') {
                $getrepeatable = GSPB_get_custom_field_value($postId, $field, 'no');
                if (!empty($getrepeatable)) {
                    $getrepeatable = json_decode($getrepeatable, true);
                }
                if (!is_array($getrepeatable)) {
                    $getrepeatable = [];
                }
            } else if ($additional == 'api_request') {
                $cache = false;
                $field = gl_dynamic_placeholders($field, [], 0);
                $filters = json_decode(urldecode($field), true);
                $apiUrl = !empty($filters['apiUrl']) ? $filters['apiUrl'] : '';
				$apiUrlcache = !empty($filters['apiUrlcache']) ? $filters['apiUrlcache'] : '';
				$apiUrltransient = !empty($filters['apiUrltransient']) ? $filters['apiUrltransient'] : '';
				if(!empty($apiUrlcache) && !empty($apiUrltransient)){
					$transient_name = trim($apiUrltransient);
					$transient_value = get_transient($transient_name);
					if (false === $transient_value) {
						$getrepeatable = \gspb_api_connector_run($filters);
					}else{
						$getrepeatable = $transient_value;
						$cache = true;
					}
				}else{
					if(!empty($apiUrl)){
						$getrepeatable = \gspb_api_connector_run($filters);
					}
				}
                if (!is_array($getrepeatable)) {
                    $getrepeatable = [];
                } else{
					$singleData = !empty($filters['apiSingleData']) ? $filters['apiSingleData'] : '';
					if(!empty($singleData)){
						$getrepeatable = [$getrepeatable];
					}
				}
                if(!empty($getrepeatable) && is_array($getrepeatable) && !empty($getrepeatable[0]['post_title']) && !empty($getrepeatable[0]['post_content']) && !empty($getrepeatable[0]['ID']) && !$cache){
                    $posts = [];
                    foreach ($getrepeatable as $key => $value) {
                        $ID = $value['ID'];
                        $AuthorID = $value['post_author'];
                        $posts[$key] = (array) $value;
                        $posts[$key]['thumbnail_url'] = get_the_post_thumbnail_url($ID, 'full');
                        $posts[$key]['author'] = get_the_author_meta('display_name', $AuthorID);
                        $posts[$key]['date'] = get_the_date('', $ID);
                        $posts[$key]['modified_date'] = get_the_modified_date('', $ID);
                        $posts[$key]['permalink'] = get_the_permalink($ID);
                        $posts[$key]['avatar_url'] = get_avatar_url($AuthorID);

                        $remove_keys = [
                            'post_author',
                            'post_date',
                            'post_date_gmt',
                            'post_status',
                            'comment_status',
                            'ping_status',
                            'post_password',
                            'to_ping',
                            'pinged',
                            'post_modified',
                            'post_modified_gmt',
                            'post_parent',
                            'menu_order',
                            'post_type',
                            'post_mime_type',
                            'filter',
                            'guid'
                        ];
                        foreach ($remove_keys as $keyname) {
                            unset($posts[$key][$keyname]);
                        }
                        $custom_fields = get_post_meta($ID);
                        foreach ($custom_fields as $fieldindex => $fieldvalue) {
                            if (is_serialized($fieldvalue[0])) {
                                $fieldvalue[0] = maybe_unserialize($fieldvalue[0]);
                            }
                            if (!empty($fieldvalue[0])) {
                                $posts[$key][$fieldindex] = GSPB_field_array_to_value($fieldvalue[0], ', ');
                            }
                        }

                    }
                    $getrepeatable = $posts;
				}
                // Get Proper WP JSON Data
				if(!empty($getrepeatable) && is_array($getrepeatable) && !empty($getrepeatable[0]['title']['rendered']) && !$cache){
					$posts = [];
                    $media_ids = [];
					foreach ($getrepeatable as $key => $value) {
						$posts[$key] = (array) $value;
						$posts[$key]['title'] = $value['title']['rendered'];
						$posts[$key]['content'] = $value['content']['rendered'];
                        $posts[$key]['excerpt'] = $value['content']['rendered'];
						$posts[$key]['modified_date'] = get_date_from_gmt($value['modified_gmt'], get_option('date_format') . ' ' . get_option('time_format'));
						$posts[$key]['date'] = get_date_from_gmt($value['date_gmt'], get_option('date_format') . ' ' . get_option('time_format'));
						$remove_keys = [
							'modified',
							'date',
							'date_gmt',
							'status',
							'comment_status',
							'ping_status',
							'password',
							'to_ping',
							'pinged',
							'modified_gmt',
							'post_modified_gmt',
							'parent',
							'order',
							'type',
							'mime_type',
							'filter',
							'guid',
							'excerpt',
                            'featured_media'
						];
						foreach ($remove_keys as $keyname) {
							unset($posts[$key][$keyname]);
						}
                        // Get Featured Media
                        if (!empty($value['featured_media']) && $value['featured_media'] != 0) {
                            $media_ids[] = $value['featured_media'];
                        }
					}
                    if(!empty($media_ids)){
                        $media_ids = array_unique($media_ids);
                        $id_query = implode( ',', $media_ids );
                        $domain = parse_url($apiUrl, PHP_URL_HOST);
						$media_count = count($media_ids);
                        $media_api_url = "https://{$domain}/wp-json/wp/v2/media?include={$id_query}&per_page={$media_count}";
                        
                        $media_response = wp_safe_remote_get($media_api_url);
                        if (!is_wp_error($media_response)) {
                            $media_items = json_decode(wp_remote_retrieve_body($media_response), true);
                            
                            // Step 3: Build a mapping where key = media id and value = source URL
                            $media_mapping = [];
                            if (!empty($media_items) && is_array($media_items)) {
                                foreach ($media_items as $media_item) {
                                    if (!empty($media_item['id']) && !empty($media_item['source_url'])) {
                                        $media_mapping[$media_item['id']] = $media_item['source_url'];
                                    }
                                }
                            }
                            
                            // Now update your posts with the corresponding thumbnail URLs
                            foreach ($posts as $key => $value) {
                                if (!empty($value['featured_media']) && isset($media_mapping[$value['featured_media']])) {
                                    $posts[$key]['thumbnail_url'] = $media_mapping[$value['featured_media']];
                                }
                            }
                        }
                    }
					$getrepeatable = $posts;
				}
                if(!empty($apiUrlcache) && !empty($apiUrltransient) && !empty($getrepeatable) && is_array($getrepeatable) && !$cache){
					set_transient($transient_name, $getrepeatable, $apiUrlcache);
				}
            } else if ($additional == 'query_args') {
				$getrepeatable = [];
				$filters = json_decode(urldecode($field), true);
				$filters = greenshiftquery_sanitize_multi_array($filters);
				$query_args = !empty($filters) ? $filters : [];
				if(!empty($query_args)){
					$getrepeatable = get_posts($query_args);
				}
                if (!is_array($getrepeatable)) {
                    $getrepeatable = [];
                }
				if(!empty($getrepeatable) && is_array($getrepeatable)){
                    $posts = [];
                    foreach ($getrepeatable as $key => $value) {
						$ID = $value->ID;
						$AuthorID = $value->post_author;
						$posts[$key] = (array) $value;
						$posts[$key]['thumbnail_url'] = get_the_post_thumbnail_url($ID, 'full');
						$posts[$key]['author'] = get_the_author_meta('display_name', $AuthorID);
						$posts[$key]['date'] = get_the_date('', $ID);
						$posts[$key]['modified_date'] = get_the_modified_date('', $ID);
						$posts[$key]['permalink'] = get_the_permalink($ID);
                        $posts[$key]['avatar_url'] = get_avatar_url($AuthorID);

						$remove_keys = [
							'post_author',
							'post_date',
							'post_date_gmt',
							'post_status',
							'comment_status',
							'ping_status',
							'post_password',
							'to_ping',
							'pinged',
							'post_modified',
							'post_modified_gmt',
							'post_parent',
							'menu_order',
							'post_type',
							'post_mime_type',
							'filter',
							'guid'
						];
						foreach ($remove_keys as $keyname) {
							unset($posts[$key][$keyname]);
						}
						$custom_fields = get_post_meta($ID);
						foreach ($custom_fields as $fieldindex => $fieldvalue) {
							if (is_serialized($fieldvalue[0])) {
								$fieldvalue[0] = maybe_unserialize($fieldvalue[0]);
							}
							if (!empty($fieldvalue[0])) {
								$posts[$key][$fieldindex] = GSPB_field_array_to_value($fieldvalue[0], ', ');
							}
						}

                    }
                    $getrepeatable = $posts;
				}
            } else if ($additional == 'idstourl') {
                $getrepeatable = GSPB_get_custom_field_value($postId, $field, 'flatarray');
                $arrays = [];
                if (!empty($getrepeatable) && is_array($getrepeatable)) {
                    foreach($getrepeatable as $key => $value){
                        $attachment_id = $value;
                        $attachment = get_post($attachment_id);
                        $attachment_meta = wp_get_attachment_metadata($attachment_id);
                        $attachment_url = wp_get_attachment_url($attachment_id);
                        $attachment_alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
                        $attachment_title = $attachment->post_title;
                        $attachment_caption = $attachment->post_excerpt;
                        $attachment_description = $attachment->post_content;
                        $attachment_mime_type = $attachment->post_mime_type;
                        $attachment_date = $attachment->post_date;
                        $attachment_modified = $attachment->post_modified;
                        
                        $arrays[$key] = [
                            'id' => $attachment_id,
                            'url' => $attachment_url,
                            'title' => $attachment_title,
                            'alt' => $attachment_alt,
                            'caption' => $attachment_caption,
                            'description' => $attachment_description,
                            'mime_type' => $attachment_mime_type,
                            'date' => $attachment_date,
                            'modified' => $attachment_modified,
                            'meta' => $attachment_meta
                        ];
                    }
                }
                $getrepeatable = $arrays;
            } else {
                $getrepeatable = GSPB_get_custom_field_value($postId, $field, 'flatarray');
            }
            if (!empty($getrepeatable) && is_string($getrepeatable)) {
                $decoded = json_decode($getrepeatable, true);
                if (json_last_error() === JSON_ERROR_NONE) {
                    $getrepeatable = $decoded;
                }
            }
            if (!empty($getrepeatable) && is_array($getrepeatable)) {
                if (array_keys($getrepeatable) === range(0, count($getrepeatable) - 1)) {
                    // Check if all values are scalar (string, number, boolean)
                    $all_scalar = true;
                    foreach ($getrepeatable as $value) {
                        if (!is_scalar($value)) {
                            $all_scalar = false;
                            break;
                        }
                    }
                    
                    if ($all_scalar) {
                        // Sequential array with scalar values only
                        $arrays = [];
                        foreach ($getrepeatable as $key => $value) {
                            $arrays[$key]['index'] = $value;
                        }
                        $getrepeatable = $arrays;
                    }
                }
                $result = $getrepeatable;
            }
            $result = !empty($result) ? $result : [
                [
                    'title' => 'Select a field',
                    'content' => 'Test content 1',
                    'image' => GL_DIR_URL.'assets/placeholder.svg',
                    'link' => '#'
                ],
                [
                    'title' => 'Select a field',
                    'content' => 'Test content 2',
                    'image' => GL_DIR_URL.'assets/placeholder.svg',
                    'link' => '#'
                ],
                [
                    'title' => 'Select a field',
                    'content' => 'Test content 3',
                    'image' => GL_DIR_URL.'assets/placeholder.svg',
                    'link' => '#'
                ]
            ];
            $result = apply_filters('gspb_repeater_args_id', $result, $postId);
            break;
        case 'meta_repeater_inner':
            $result = [];
            $getrepeatable = $field;
            if ($additional == 'relationpostobj' || $additional == 'relationpostids') {
                if ($additional == 'relationpostids') {
                    if (!empty($getrepeatable) && !is_array($getrepeatable)) {
                        $ids = wp_parse_id_list($getrepeatable);
                    } else {
                        $ids = $getrepeatable;
                    }
                    if (!empty($ids)) {
                        $args = array(
                            'post__in' => $ids,
                            'numberposts' => '-1',
                            'orderby' => 'post__in',
                            'ignore_sticky_posts' => 1,
                            'post_type' => 'any'
                        );
                        $getrepeatable = get_posts($args);
                    }
                }
                if (!empty($getrepeatable)) {
                    if (!is_array($getrepeatable)) {
                        $getrepeatable = [$getrepeatable];
                    }
                    $posts = [];
                    foreach ($getrepeatable as $key => $value) {
                        if (is_object($value) && !empty($value->ID)) {
                            $posts[$key] = (array) $value;
                            $posts[$key]['thumbnail_url'] = get_the_post_thumbnail_url($value->ID, 'full');
                            $posts[$key]['author'] = get_the_author_meta('display_name', $value->post_author);
                            $posts[$key]['date'] = get_the_date('', $value->ID);
                            $posts[$key]['modified_date'] = get_the_modified_date('', $value->ID);
                            $posts[$key]['permalink'] = get_the_permalink($value->ID);
                            $posts[$key]['avatar_url'] = get_avatar_url($value->post_author);

                            $remove_keys = [
                                'post_author',
                                'post_date',
                                'post_date_gmt',
                                'post_status',
                                'comment_status',
                                'ping_status',
                                'post_password',
                                'to_ping',
                                'pinged',
                                'post_modified',
                                'post_modified_gmt',
                                'post_parent',
                                'menu_order',
                                'post_type',
                                'post_mime_type',
                                'filter',
                                'guid'
                            ];
                            foreach ($remove_keys as $keyname) {
                                unset($posts[$key][$keyname]);
                            }
                            $custom_fields = get_post_meta($value->ID);
                            foreach ($custom_fields as $fieldindex => $fieldvalue) {
                                if (is_serialized($fieldvalue[0])) {
                                    $fieldvalue[0] = maybe_unserialize($fieldvalue[0]);
                                }
                                if (!empty($fieldvalue[0])) {
                                    $posts[$key][$fieldindex] = GSPB_field_array_to_value($fieldvalue[0], ', ');
                                }
                            }
                        }
                    }
                    $getrepeatable = $posts;
                }
            }
            if (!empty($getrepeatable) && is_array($getrepeatable)) {
                $result = $getrepeatable;
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'site_data':
            if ($field == 'name' || $field == 'description') {
                $result = get_bloginfo($field);
            } else if ($field == 'year') {
                $result = date_i18n("Y");
            } else if ($field == 'month') {
                $result = date_i18n("F");
            } else if ($field == 'today') {
                $next_day = strtotime("today", current_time('timestamp'));
                $result = wp_date(get_option('date_format'), $next_day);
            } else if ($field == 'todayplus1') {
                $next_day = strtotime("+1 day", current_time('timestamp'));
                $result = wp_date(get_option('date_format'), $next_day);;
            } else if ($field == 'todayplus2') {
                $next_day = strtotime("+2 days", current_time('timestamp'));
                $result = wp_date(get_option('date_format'), $next_day);;
            } else if ($field == 'todayplus3') {
                $next_day = strtotime("+3 days", current_time('timestamp'));
                $result = wp_date(get_option('date_format'), $next_day);;
            } else if ($field == 'todayplus7') {
                $next_day = strtotime("+7 days", current_time('timestamp'));
                $result = wp_date(get_option('date_format'), $next_day);;
            } else if ($field == 'siteoption') {
                $field = esc_attr($additional);
                $result = GSPB_get_custom_field_value(0, $field, ', ', 'option');
            } else if ($field == 'acfsiteoption') {
                $field = esc_attr($additional);
                $result = GSPB_get_custom_field_value(0, $field, ', ', 'acf_option');
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'post_data':
            if($field == 'post_image'){
                $size = 'full';
                if(!empty($additional2)){
                    $size = esc_attr($additional2);
                }
                $result = get_the_post_thumbnail_url($postId, $size);
            }else if($field == 'post_author'){
                $author_id = get_post_field('post_author', $postId); 
                $author_name = get_the_author_meta('display_name', $author_id);
                $result = $author_name;
            } else if ($field == 'post_date') {
                $result = get_the_date('', $postId);
            } else if ($field == 'post_modified') {
                $result = get_the_modified_date('', $postId);
            }else {
                $resultObj = get_post($postId);
                if (is_object($resultObj)) {
                    $result = $resultObj->$field;
                }
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'author_data':
        case 'authordata':
            $resultObj = get_post($postId);
            if (is_object($resultObj)) $result = $resultObj->post_author;
            if ($result) {
                $result = get_the_author_meta($field, $result);
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'author_meta':
            $resultObj = get_post($postId);
            if (is_object($resultObj)) $resultId = $resultObj->post_author;
            if ($resultId) {
                $result = get_user_meta($resultId, $field, true);
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'user_data':
            $resultId = get_current_user_id();
            if ($resultId && $field && $resultId > 0) {
                $result = get_the_author_meta($field, $resultId);
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'user_meta':
            $resultId = get_current_user_id();
            if ($resultId && $field && $resultId > 0) {
                $result = get_user_meta($resultId, $field, true);
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'meta_field_get_image':
            $result = GSPB_get_custom_field_value($postId, $field, 'no');
            if (is_numeric($result)) $result = wp_get_attachment_url($result);
            $result = !empty($result) ? $result : '';
            break;
        case 'taxonomy_field_get_image':
            $result = get_term_meta($postId, $field, true);
            if (is_array($result)) $result = $result[0];
            if (is_numeric($result)) $result = wp_get_attachment_url($result);
            $result = !empty($result) ? $result : '';
            break;
        case 'taxonomy_value':
            $terms = get_the_terms($postId, $field);
            $divider = ', ';
            if ($additional) $divider = '<span class="gspb_tax_spacer">' . $additional . '</span>';
            if ($terms && !is_wp_error($terms)) {
                $term_slugs_arr = array();
                foreach ($terms as $term) {
                    $term_slugs_arr[] = '' . $term->name . '';
                }
                $terms_slug_str = join($divider, $term_slugs_arr);
                $result = $terms_slug_str;
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'taxonomy_link':
            $divider = ', ';
            if ($additional) $divider = '<span class="gspb_tax_spacer">' . $additional . '</span>';
            $term_list = get_the_term_list($postId, $field, '', $divider, '');
            if (!is_wp_error($term_list)) {
                $result = $term_list;
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'meta_field_get_text':
            $result = GSPB_get_custom_field_value($postId, $field);
            $result = !empty($result) ? $result : '';
            break;
        case 'meta_field_get_map':
            $result = GSPB_get_custom_field_value($postId, $field, 'flat');
            if (is_array($result)) {
                $map = [];
                if (isset($result['location']) && isset($result['location']['lat']) && isset($result['location']['lng'])) {
                    $map['lat'] = $result['location']['lat'];
                    $map['lng'] = $result['location']['lng'];
                    $map['lang'] = $result['location']['lng'];
                    $map['title'] = !empty($result['title']) ? $result['title'] : '';
                    $map['description'] = !empty($result['description']) ? $result['description'] : '';
                } else if (isset($result['lat']) && isset($result['lng'])) {
                    $map['lat'] = $result['lat'];
                    $map['lng'] = $result['lng'];
                    $map['lang'] = $result['lng'];
                } else if (isset($result['latitude']) && isset($result['longitude'])) {
                    $map['lat'] = $result['latitude'];
                    $map['lng'] = $result['longitude'];
                    $map['lang'] = $result['longitude'];
                }
                $result = $map;
            }
            $result = !empty($result) ? $result : '';
            break;
        case 'taxonomy_field_get_text':
            if ($field == 'name') {
                $taxobj = get_term($postId);
                if (!is_wp_error($taxobj)) {
                    $result = $taxobj->name;
                }
            } else if ($field == 'description') {
                $taxobj = get_term($postId);
                if (!is_wp_error($taxobj)) {
                    $result = $taxobj->description;
                }
            } else {
                $result = get_term_meta($postId, $field, true);
            }
            if (is_array($result)) $result = $result[0];
            $result = !empty($result) ? $result : '';
            break;
        default:
            break;
    }

    return json_encode($result);
}

function gspb_get_user_roles(WP_REST_Request $request)
{
    $search = sanitize_text_field($request->get_param('search'));

    global $wp_roles;

    $res = [];

    foreach ($wp_roles->roles as $key => $role) {
        if (empty($search) || strpos(strtolower($role['name']), strtolower($search)) !== false) {
            $res[] = ['label' => $role['name'], 'id' => $key, 'value' => $key];
        }
    }

    return json_encode($res);
}


function gspb_get_taxonomy_terms_search(WP_REST_Request $request)
{
    $taxonomy = sanitize_text_field($request->get_param('taxonomy'));
    $search = sanitize_text_field($request->get_param('search'));
    $search_id = sanitize_text_field($request->get_param('search-id'));

    global $wpdb;
    if (empty($search_id)) {
        $query = [
            "select" => "SELECT SQL_CALC_FOUND_ROWS a.term_id AS id, b.name as name, b.slug AS slug
        FROM {$wpdb->term_taxonomy} AS a
        INNER JOIN {$wpdb->terms} AS b ON b.term_id = a.term_id",
            "where" => "WHERE a.taxonomy = '{$taxonomy}'",
            "like" => "AND (b.slug LIKE '%s' OR b.name LIKE '%s' )",
            "offset" => "LIMIT %d, %d"
        ];

        $search_term = '%' . $wpdb->esc_like($search) . '%';
        $offset = 0;
        $search_limit = 100;

        $final_query = $wpdb->prepare(implode(' ', $query), $search_term, $search_term, $offset, $search_limit);
    } else {
        $search_id = rtrim($search_id, ',');
        $query = [
            "select" => "SELECT SQL_CALC_FOUND_ROWS a.term_id AS id, b.name as name, b.slug AS slug
        FROM {$wpdb->term_taxonomy} AS a
        INNER JOIN {$wpdb->terms} AS b ON b.term_id = a.term_id",
            "where" => "WHERE a.taxonomy = '{$taxonomy}'",
            "like" => "AND a.term_id IN({$search_id})",
            "offset" => "LIMIT %d, %d"
        ];

        $offset = 0;
        $search_limit = 100;

        $final_query = $wpdb->prepare(implode(' ', $query), $offset, $search_limit);
    }
    // Return saved values

    $results = $wpdb->get_results($final_query);

    $total_results = $wpdb->get_row("SELECT FOUND_ROWS() as total_rows;");
    $response_data = [];

    if ($results) {
        foreach ($results as $result) {
            $response_data[] = [
                'slug' => esc_html($result->slug),
                'name' => esc_html($result->name),
                'id' => (int)$result->id
            ];
        }
    }

    return json_encode($response_data);
}

function gspb_get_terms(WP_REST_Request $request)
{
    $taxonomy = sanitize_text_field($request->get_param('taxonomy'));
    $show_empty = boolval(sanitize_text_field($request->get_param('show_empty')));
    $order_by = sanitize_text_field($request->get_param('order_by'));
    $order = sanitize_text_field($request->get_param('order'));
    $include = sanitize_text_field($request->get_param('include'));
    $exclude = sanitize_text_field($request->get_param('exclude'));
    $hierarchy = boolval(sanitize_text_field($request->get_param('hierarchy')));
    $image_meta = sanitize_text_field($request->get_param('image_meta'));
    $number = intval($request->get_param('number'));
    $childOf = sanitize_text_field($request->get_param('childof'));
    $image_size = sanitize_text_field($request->get_param('image_size'));

    $terms = GSPB_Postfilters::get_terms(
        $taxonomy,
        $show_empty,
        $order_by,
        $order,
        $include,
        $exclude,
        $hierarchy,
        false,
        false,
        $image_meta,
        $number,
        $image_size,
        $childOf
    );
    $json = json_encode($terms, JSON_FORCE_OBJECT);

    return $json;
}

function gspb_get_taxonomies(WP_REST_Request $request)
{
    $post_type = sanitize_text_field($request->get_param('post_type'));

    $result = GSPB_Postfilters::get_taxonomies($post_type);

    return json_encode($result);
}

function gspb_get_all_taxonomies()
{
    $exclude_list = array_flip([
        'nav_menu',
        'link_category',
        'post_format',
        'elementor_library_type',
        'elementor_library_category',
        'action-group'
    ]);
    $response_data = [];
    $args = [];
    foreach (get_taxonomies($args, 'objects') as $taxonomy => $object) {
        if (isset($exclude_list[$taxonomy])) {
            continue;
        }

        $taxonomy = esc_html($taxonomy);
        $response_data[] = [
            'value' => $taxonomy,
            'label' => esc_html($object->label),
        ];
    }
    return json_encode($response_data);
}

function gspb_get_post_types()
{
    $post_types = get_post_types(['public' => true], 'objects');
    $result = [];

    foreach ($post_types as $post_type) {
        $result[] = ['value' => $post_type->name, 'label' => $post_type->label];
    }

    return json_encode($result);
}

function gspb_get_post_metas(WP_REST_Request $request)
{
    $post_type = sanitize_text_field($request->get_param('post_type'));
    if (empty($post_type)) $post_type = 'post';
    $exclude_empty = false;
    $exclude_hidden = false;
    global $wpdb;
    $query = "
        SELECT DISTINCT($wpdb->postmeta.meta_key)
        FROM $wpdb->posts
        LEFT JOIN $wpdb->postmeta
        ON $wpdb->posts.ID = $wpdb->postmeta.post_id
        WHERE $wpdb->posts.post_type = '%s'
        ";
    if ($exclude_empty)
        $query .= " AND $wpdb->postmeta.meta_key != ''";
    if ($exclude_hidden)
        $query .= " AND $wpdb->postmeta.meta_key NOT RegExp '(^[_0-9].+$)' ";

    $meta_keys = $wpdb->get_col($wpdb->prepare($query, $post_type));

    $result = [];
    foreach ($meta_keys as $meta_key) {
        $result[] = ['value' => $meta_key, 'label' => $meta_key];
    }

    return json_encode($result);
}

if (!function_exists('gspb_get_user_ip')) {
    function gspb_get_user_ip()
    {
        foreach (
            array(
                'HTTP_CLIENT_IP',
                'HTTP_X_FORWARDED_FOR',
                'HTTP_X_FORWARDED',
                'HTTP_X_CLUSTER_CLIENT_IP',
                'HTTP_FORWARDED_FOR',
                'HTTP_FORWARDED',
                'REMOTE_ADDR'
            ) as $key
        ) {
            if (array_key_exists($key, $_SERVER) === true) {
                $ip = sanitize_text_field(wp_unslash($_SERVER[$key]));
                if (strpos($ip, ',') !== false) {
                    $ip = explode(',', $ip);
                    $ip = trim($ip[0]);
                }
                // Validate as IP address
                $ip = filter_var($ip, FILTER_VALIDATE_IP);
                if ($ip) {
                    return $ip;
                }
            }
        }
        return '127.0.0.3';
    }
}


function gspb_get_dynamic_part_callback(WP_REST_Request $request){
    $dynamic_text = !empty($request->get_param('dynamic_text')) ? sanitize_text_field($request->get_param('dynamic_text')) : '';
    $attributetext = !empty($request->get_param('attributetext')) ? sanitize_text_field($request->get_param('attributetext')) : '';
    $postID = !empty($request->get_param('postID')) ? sanitize_text_field($request->get_param('postID')) : '';
    $attribute = !empty($request->get_param('attribute')) ? json_decode(sanitize_text_field($request->get_param('attribute')), true) : '';
    if(!empty($attribute) && is_array($attribute) && $postID){
        $attribute['dynamicPostId'] = $postID;
        $attribute['dynamicSource'] = 'definite_item';
    }

    $result = GSPB_make_dynamic_text($dynamic_text, '', '', $attribute, $attributetext);
    return json_encode($result);
}

function GSPB_make_dynamic_text($dynamic_text, $attrs, $block, $attribute, $attributetext = '')
{

    $textoriginal = $attributetext;
    $text_replace = '';
    $postid = '';

    $repeaterField = !empty($attribute['repeaterField']) ? $attribute['repeaterField'] : '';
    if ($repeaterField && !empty($attribute['repeaterArray'])) {
        $text_replace = GSPB_get_value_from_array_field($repeaterField, $attribute['repeaterArray']);
        if(!empty($text_replace)){
            if(!empty($attribute['postprocessor']) && $attribute['postprocessor'] == 'json'){
                //do nothing. Add json postprocessor below
            }else{
                $text_replace = GSPB_field_array_to_value($text_replace, ', ');
            }
        }
    } else {
        if (!empty($attribute['dynamicSource']) && $attribute['dynamicSource'] == 'definite_item') {
            if ($attribute['dynamicType'] == 'taxonomy') {
                $postid = $attribute['dynamicTaxonomyId'];
            } else {
                if (!empty($attribute['dynamicPostId'])) {
                    $postid = (int)$attribute['dynamicPostId'];
                }
            }
        } else {
            if (empty($attribute['dynamicType'])) return $dynamic_text;
            if (!empty($attribute['dynamicType']) && $attribute['dynamicType'] == 'taxonomy') {
                if (is_tax() || is_category() || is_tag()) {
                    $postid = get_queried_object()->term_id;
                }
            } else {
                global $post;
                if (is_object($post)) {
                    $postid = $post->ID;
                }
            }
        }
        if ($attribute['dynamicType'] === 'custom') {
            if (!$postid) return $dynamic_text;
            $fieldvalue = '';
            if (!empty($attribute['dynamicField'])) {
                $field = esc_attr($attribute['dynamicField']);
                $fieldvalue = GSPB_get_custom_field_value($postid, $field);
                $text_replace = $fieldvalue;
            }
        } else if ($attribute['dynamicType'] == 'sitedata' && !empty($attribute['dynamicSiteData'])) {
            $field = esc_attr($attribute['dynamicSiteData']);

            if ($field == 'name' || $field == 'description') {
                $text_replace = get_bloginfo($field);
            } else if ($field == 'year') {
                $text_replace = date_i18n("Y");
            } else if ($field == 'month') {
                $text_replace = date_i18n("F");
            } else if ($field == 'today') {
                $next_day = strtotime("today", current_time('timestamp'));
                $text_replace = wp_date(get_option('date_format'), $next_day);
            } else if ($field == 'todayplus1') {
                $next_day = strtotime("+1 day", current_time('timestamp'));
                $text_replace = wp_date(get_option('date_format'), $next_day);
            } else if ($field == 'todayplus2') {
                $next_day = strtotime("+2 days", current_time('timestamp'));
                $text_replace = wp_date(get_option('date_format'), $next_day);
            } else if ($field == 'todayplus3') {
                $next_day = strtotime("+3 days", current_time('timestamp'));
                $text_replace = wp_date(get_option('date_format'), $next_day);
            } else if ($field == 'todayplus7') {
                $next_day = strtotime("+7 days", current_time('timestamp'));
                $text_replace = wp_date(get_option('date_format'), $next_day);
            } else if ($field == 'transient') {
                $field = esc_attr($attribute['dynamicField']);
                if (!$field) return;
                $text_replace = GSPB_get_custom_field_value(0, $field, ', ', 'transient');
            } else if ($field == 'querystring') {
                $field = esc_attr($attribute['dynamicField']);
                if (!$field) return;
                $text_replace = GSPB_get_custom_field_value(0, $field, ', ', 'querystring');
            } else if ($field == 'siteoption') {
                $field = esc_attr($attribute['dynamicField']);
                if (!$field) return;
                $text_replace = GSPB_get_custom_field_value(0, $field, ', ', 'option');
            } else if ($field == 'acfsiteoption') {
                $field = esc_attr($attribute['dynamicField']);
                if (!$field) return;
                $text_replace = GSPB_get_custom_field_value(0, $field, ', ', 'acf_option');
            }
        } else if ($attribute['dynamicType'] == 'authordata' && !empty($attribute['dynamicAuthorData'])) {
            if (!$postid) return $dynamic_text;
            $field = esc_attr($attribute['dynamicAuthorData']);
            $resultObj = get_post($postid);
            if (is_object($resultObj)) {
                $authorID = $resultObj->post_author;
                if ($field == 'meta' && !empty($attribute['dynamicAuthorField'])) {
                    $text_replace = get_user_meta($authorID, $attribute['dynamicAuthorField'], true);
                } else if($field == 'user_avatar_url'){
                    $text_replace = get_avatar_url($authorID, ['size' => !empty($attribute['avatarSize']) ? (int)$attribute['avatarSize'] : 96]);
                } else {
                    $text_replace = get_the_author_meta($field, $authorID);
                }
            }
        }else if ($attribute['dynamicType'] == 'user_data' && !empty($attribute['dynamicAuthorData'])) {
            $userId = get_current_user_id();
            $field = esc_attr($attribute['dynamicAuthorData']);
            if ($userId && $field) {
                if ($field == 'meta' && !empty($attribute['dynamicAuthorField'])) {
                    $text_replace = get_user_meta($userId, $attribute['dynamicAuthorField'], true);
                }else if($field == 'user_avatar_url'){
                    $text_replace = get_avatar_url($userId, ['size' => !empty($attribute['avatarSize']) ? (int)$attribute['avatarSize'] : 96]);
                } else {
                    $text_replace = get_the_author_meta($field, $userId);
                }
            }
        } else if ($attribute['dynamicType'] == 'taxonomyvalue' && !empty($attribute['dynamicTaxonomyValue'])) {
            if (!isset($postid) || !$postid) return $dynamic_text;
            $field = esc_attr($attribute['dynamicTaxonomyValue']);
            $divider = !empty($attribute['dynamicTaxonomyDivider']) ? '<span class="gspb_tax_spacer">' .
                $attribute['dynamicTaxonomyDivider'] . '</span>' : ', ';
            $linkrender = !empty($attribute['dynamicTaxonomyLink']) ? $attribute['dynamicTaxonomyLink'] : false;
            if ($linkrender) {
                $term_list = get_the_term_list($postid, $field, '', $divider, '');
                if (!is_wp_error($term_list)) {
                    $text_replace = $term_list;
                }
            } else {
                $terms = get_the_terms($postid, $field);
                if ($terms && !is_wp_error($terms)) {
                    $term_slugs_arr = array();
                    foreach ($terms as $term) {
                        $term_slugs_arr[] = '' . $term->name . '';
                    }
                    $terms_slug_str = join($divider, $term_slugs_arr);
                    $text_replace = $terms_slug_str;
                }
            }
        } else if ($attribute['dynamicType'] == 'postdata' && !empty($attribute['dynamicPostData'])) {
            if (empty($postid)) return $dynamic_text;
            $field = esc_attr($attribute['dynamicPostData']);

            if($field == 'post_image'){
                $size = 'full';
                if(!empty($attribute['dynamicPostImageSize'])){
                    $size = esc_attr($attribute['dynamicPostImageSize']);
                }
                $text_replace = get_the_post_thumbnail_url($postid, $size);
            } else if($field == 'post_author'){
                $author_id = get_post_field('post_author', $postid); 
                $author_name = get_the_author_meta('display_name', $author_id);
                $text_replace = $author_name;
            } else if ($field == 'post_date') {
                $text_replace = get_the_date('', $postid);
            } else if ($field == 'post_modified') {
                $text_replace = get_the_modified_date('', $postid);
            } else if ($field == 'permalink') {
                $text_replace = get_the_permalink($postid);
            }else if ($field == 'post_parent_title') {
                $parentId = wp_get_post_parent_id($postid);
                if ($parentId) {
                    $text_replace = get_the_title($parentId);
                } else {
                    $text_replace = ''; // No parent post
                }
            }else if($field == 'product_image_gallery_1'){
                $gallery = get_post_meta($postid, '_product_image_gallery', true);
                if(!empty($gallery)){
                    $gallery = explode(',', $gallery);
                    $text_replace = !empty($gallery[1]) ? wp_get_attachment_url($gallery[1]) : '';
                }

            } else if($field == 'post_parent_link'){
                $parentId = wp_get_post_parent_id($postid);
                if ($parentId) {
                    $text_replace = get_the_permalink($parentId);
                } else {
                    $text_replace = ''; // No parent post
                }
            } else if ($field == 'fullcontent') {
                $text_replace = get_the_content(null, null, $postid);
            } else if ($field == 'fullcontentfilters') {
                if (is_admin()) {
                    $text_replace = '<p>This is the Content block, it will display all the blocks in any single post or page.</p><p>That might be a simple arrangement like consecutive paragraphs in a blog post, or a more elaborate composition that includes image galleries, videos, tables, columns, and any other block types.</p><p>If there are any Custom Post Types registered at your site, the Content block can display the contents of those entries as well.</p>';
                } else {
                    $content = get_the_content(null, null, $postid);
                    $content = do_shortcode($content);
                    $text_replace = wpautop($content);
                }
            }else {
                $resultObj = get_post($postid);
                if (is_object($resultObj)) {
                    $text_replace = $resultObj->$field;
                }
            }
        } else if ($attribute['dynamicType'] == 'taxonomy' && !empty($attribute['dynamicTaxonomyField'])) {
            if (!isset($postid) || !$postid) return $dynamic_text;
            $field = esc_attr($attribute['dynamicTaxonomyField']);

            if ($field == 'name') {
                $taxobj = get_term($postid);
                if (!is_wp_error($taxobj)) {
                    $fieldvalue = $taxobj->name;
                }
            } else if ($field == 'description') {
                $taxobj = get_term($postid);
                if (!is_wp_error($taxobj)) {
                    $fieldvalue = $taxobj->description;
                }
            } else {
                $fieldvalue = GSPB_get_custom_field_value($postid, $field, 'flatarray', 'taxonomymetabyid');
            }
            if (is_array($fieldvalue)) $fieldvalue = $fieldvalue[0];
            $text_replace = $fieldvalue;
        }
    }

    if (!empty($attribute['postprocessor'])) {
        $text_replace = gspb_data_post_processor($text_replace, $attribute['postprocessor'], $postid, $replaceLabel='', $icon='');
        if($attribute['postprocessor'] == 'datecustom' && !empty($attribute['dateformat'])){
            $dateformat = $attribute['dateformat'];
            $text_replace = date(
                $dateformat,
                is_numeric($text_replace) ? $text_replace : strtotime($text_replace)
            );
        }
    }

    if ($text_replace && $textoriginal) {
        if (!is_string($dynamic_text)) {
            return $text_replace;
        }
        $dynamic_text = str_replace('$', '\$', $dynamic_text);
        // Use preg_replace_callback to handle the replacement safely
		$pattern = '/<dynamictext>.*?<\/dynamictext>/s';  // Added 's' modifier for multiline
        return preg_replace_callback($pattern, 
            function($matches) use ($text_replace) {
                return $text_replace;
            }, 
            $dynamic_text
        );
    } else if ($text_replace) {
        if ($dynamic_text && is_string($dynamic_text) && strpos($dynamic_text, "{DYNAMIC}") !== false) {
            $pattern = '/{DYNAMIC}/';
            $text_replace = preg_replace($pattern,$text_replace, $dynamic_text);
        }
        return $text_replace;
    }else if(!$text_replace && !$textoriginal){
        if(!empty($attribute['fallbackValue'])){
            return $attribute['fallbackValue'];
        }
        return '';
    }
    if(!empty($attribute['fallbackValue'])){
        return $attribute['fallbackValue'];
    } else {
        return $dynamic_text;
    }
}


function GSPB_make_dynamic_flatvalue($html, $attrs, $block, $attribute, $attributereplace, $return =
false)
{
    $textoriginal = $attributereplace;
    $value = '';

    global $post;
    if (is_object($post)) {
        $postid = $post->ID;
    }
    if (!empty($attrs['dynamicType']) && $attrs['dynamicType'] == 'permatext') {
    } else {
        if (!empty($attribute) && $postid) {
            $field = esc_attr($attribute);
            $value = GSPB_get_custom_field_value($postid, $field);
        }
    }
    if (!$value && $return) {
        return '';
    }
    if ($return) {
        return $value;
    }
    if ($value && $textoriginal) {
        return str_replace($textoriginal, $value, $html);
    }
    return $html;
}

function GSPB_get_custom_field_value($postid, $field, $divider = ', ', $type = 'custom')
{
    if (!$field) return;
    if (strpos($field, '[') !== false) {
        $fieldarray = explode('[', $field);
        $cleanfield = $fieldarray[0];
        $pattern = '/\[(\'[^\']*\'|"[^"]*"|[^\]]*)\]/';
        preg_match_all($pattern, $field, $matches);
        $matches = $matches[1];
        if (!empty($matches) && is_array($matches)) {
            if ($type == 'option') {
                $fieldvalue = get_option($cleanfield);
            } else if ($type == 'acf_option' && function_exists('get_field')) {
                $fieldvalue = get_field($cleanfield, 'option');
            } else if ($type == 'transient') {
                $fieldvalue = get_transient($cleanfield);
            } else if ($type == 'querystring') {
                $fieldvalue = isset($_GET[$cleanfield]) ? esc_attr($_GET[$cleanfield]) : '';
            } else if ($type == 'authormeta') {
                $author_id = get_post_field('post_author', $postid);
                $fieldvalue = get_user_meta($author_id, $cleanfield, true);
            } else if ($type == 'currentusermeta') {
                $author_id = get_current_user_id();
                if ($author_id) {
                    $fieldvalue = get_user_meta($author_id, $cleanfield, true);
                } else {
                    $fieldvalue = '';
                }
            } else if ($type == 'taxonomymeta') {
                $taxonomy_id = get_queried_object_id();
                if ($taxonomy_id) {
                    $fieldvalue = get_term_meta($taxonomy_id, $cleanfield, true);
                    if(function_exists('get_field') && $fieldvalue){
                        $fieldvalue = get_field($cleanfield, 'term_'.$taxonomy_id);
                    }
                } else {
                    $fieldvalue = '';
                }
            }else if ($type == 'taxonomymetabyid') {
                $taxonomy_id = $postid;
                if ($taxonomy_id) {
                    $fieldvalue = get_term_meta($taxonomy_id, $cleanfield, true);
                    if(function_exists('get_field') && $fieldvalue){
                        $fieldvalue = get_field($cleanfield, 'term_'.$taxonomy_id);
                    }
                } else {
                    $fieldvalue = '';
                }
            } else {
                $fieldvalue = get_post_meta($postid, $cleanfield, true);
            }
            foreach ($matches as $match) {
                $match = str_replace("'", '', $match);
                $match = str_replace('"', '', $match);
                if (!is_array($fieldvalue) && strpos($fieldvalue, ',') !== false) {
                    $fieldvalue = explode(',', $fieldvalue);
                }
                if (is_array($fieldvalue) && isset($fieldvalue[$match])) {
                    $fieldvalue = $fieldvalue[$match];
                } else {
                    $fieldvalue = '';
                }
            }
        }
    } else {
        if ($type == 'option') {
            $fieldvalue = get_option($field);
        } else if ($type == 'transient') {
            $fieldvalue = get_transient($field);
        } else if ($type == 'acf_option' && function_exists('get_field')) {
            $fieldvalue = get_field($field, 'option');
        } else if ($type == 'querystring') {
            $fieldvalue = isset($_GET[$field]) ? esc_attr($_GET[$field]) : '';
        } else if ($type == 'authormeta') {
            $author_id = get_post_field('post_author', $postid);
            $fieldvalue = get_user_meta($author_id, $field, true);
        } else if ($type == 'currentusermeta') {
            $author_id = get_current_user_id();
            if ($author_id) {
                $fieldvalue = get_user_meta($author_id, $field, true);
            } else {
                $fieldvalue = '';
            }
        } else if ($type == 'taxonomymeta') {
            $taxonomy_id = get_queried_object_id();
            if ($taxonomy_id) {
                $fieldvalue = get_term_meta($taxonomy_id, $field, true);
                if(function_exists('get_field') && $fieldvalue){
                    $fieldvalue = get_field($field, 'term_'.$taxonomy_id);
                }
            } else {
                $fieldvalue = '';
            }
        } else if ($type == 'taxonomymetabyid') {
            $taxonomy_id = $postid;
            if ($taxonomy_id) {
                $fieldvalue = get_term_meta($taxonomy_id, $field, true);
                if(function_exists('get_field') && $fieldvalue){
                    $fieldvalue = get_field($field, 'term_'.$taxonomy_id);
                }
            } else {
                $fieldvalue = '';
            }
        } else {
            $fieldvalue = get_post_meta($postid, $field, true);
        }
    }
    if ($divider != 'flatarray') {
        $fieldvalue = GSPB_field_array_to_value($fieldvalue, $divider);
    }
    return apply_filters('greenshift_dynamic_field_output', $fieldvalue);
}

function GSPB_get_value_from_array_field($field, $value)
{
    if (strpos($field, '[') !== false) {
        $fieldarray = explode('[', $field);
        $cleanfield = $fieldarray[0];
        $pattern = '/\[(\'[^\']*\'|"[^"]*"|[^\]]*)\]/';
        preg_match_all($pattern, $field, $matches);
        $matches = $matches[1];
        if (!empty($matches) && is_array($matches)) {
            $fieldvalue = $value[$cleanfield];
            foreach ($matches as $match) {
                $match = str_replace("'", '', $match);
                $match = str_replace('"', '', $match);
                if (isset($fieldvalue[$match])) {
                    $fieldvalue = $fieldvalue[$match];
                }
            }
        }
    } else {
        $fieldvalue = isset($value[$field]) ? $value[$field] : '';
    }
    return apply_filters('greenshift_dynamic_field_output', $fieldvalue);
}

function GSPB_field_array_to_value($fieldvalue, $divider)
{
    if (is_array($fieldvalue)) {
        $output = '';
        foreach ($fieldvalue as $key => $value) {
            if (is_array($value)) {
                // recursively call this function to handle multi-dimensional arrays
                $value = GSPB_field_array_to_value($value, $divider);
            }
            if ($divider == 'list') {
                $output .= '<li>' . $value . '</li>';
            } else if ($divider == 'no') {
                return $value;
            } else if ($divider != 'flat') {
                $output .= $value . $divider;
            } else {
                // nothing to do
            }
        }
        if ($divider == 'list') {
            return '<ul>' . $output . '</ul>';
        } else if ($divider == 'flat' && count($fieldvalue) > 0) {
            return $fieldvalue[0];
        } else {
            return rtrim($output, $divider);
        }
    }
    return $fieldvalue;
}


//////////////////////////////////////////////////////////////////
// Quick get and show video thumbnail and embed by url
//////////////////////////////////////////////////////////////////
if (!function_exists('gs_parse_video_url')) {
    function gs_parse_video_url($url, $return = 'embed', $width = '', $height = '', $rel = 0)
    {
        $urls = parse_url($url);

        //url is http://vimeo.com/xxxx
        if ($urls['host'] == 'vimeo.com') {
            $vid = ltrim($urls['path'], '/');
        }
        //url is http://youtu.be/xxxx
        else if ($urls['host'] == 'youtu.be') {
            $yid = ltrim($urls['path'], '/');
        }
        //url is http://www.youtube.com/embed/xxxx
        else if (strpos($urls['path'], 'embed') == 1) {
            $path_parts = explode('/', $urls['path']);
            $yid = end($path_parts);
        }
        //url is xxxx only
        else if (strpos($url, '/') === false) {
            $yid = $url;
        }
        //http://www.youtube.com/watch?feature=player_embedded&v=m-t4pcO99gI
        //url is http://www.youtube.com/watch?v=xxxx
        else {
            parse_str($urls['query'], $i);
            $yid = $i['v'];
            if (!empty($feature)) {
                $parts = explode('v=', $urls['query']);
                $yid = end($parts);
                $arr = explode('&', $yid);
                $yid = $arr[0];
            }
        }
        if (isset($yid)) {

            //return embed iframe
            if ($return == 'embed') {
                return '<iframe width="' . ($width ? $width : 765) . '" height="' . ($height ? $height : 430) . '"
                    src="https://www.youtube.com/embed/' . $yid . '?rel=' . $rel . '&enablejsapi=1" frameborder="0"
                    ebkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>';
            }
            //return normal thumb
            else if ($return == 'thumb' || $return == 'thumbmed') {
                return '//i1.ytimg.com/vi/' . $yid . '/default.jpg';
            } else if ($return == 'embedurl') {
                return 'https://www.youtube.com/embed/' . $yid;
            }
            //return hqthumb
            else if ($return == 'hqthumb') {
                return '//i1.ytimg.com/vi/' . $yid . '/hqdefault.jpg';
            } else if ($return == 'maxthumb') {
                $vtrid = 'ymaxthumb_' . $yid;
                $ymaxthumblink = get_transient($vtrid);

                if ($ymaxthumblink) {
                    $image = $ymaxthumblink;
                } else {
                    $maxurl = "https://i.ytimg.com/vi/" . $yid . "/maxresdefault.jpg";
                    $max = wp_safe_remote_head($maxurl);

                    if (!is_wp_error($max) && wp_remote_retrieve_response_code($max) != '404') {
                        $image = $maxurl;
                        set_transient($vtrid, $image, 30 * DAY_IN_SECONDS);
                    } else {
                        $image = '//i1.ytimg.com/vi/' . $yid . '/hqdefault.jpg';
                        set_transient($vtrid, $image, 30 * DAY_IN_SECONDS);
                    }
                }

                return $image;
            } else if ($return == 'hoster') {
                return 'youtube';
            } else if ($return == 'data') {
                $vtrid = 'ymaxthumb_' . $yid;
                $ymaxthumblink = get_transient($vtrid);

                if ($ymaxthumblink) {
                    $image = $ymaxthumblink;
                } else {
                    $maxurl = "https://i.ytimg.com/vi/" . $yid . "/maxresdefault.jpg";
                    $max = wp_safe_remote_head($maxurl);

                    if (!is_wp_error($max) && wp_remote_retrieve_response_code($max) != '404') {
                        $image = $maxurl;
                        set_transient($vtrid, $image, 30 * DAY_IN_SECONDS);
                    } else {
                        $image = '//i1.ytimg.com/vi/' . $yid . '/hqdefault.jpg';
                        set_transient($vtrid, $image, 30 * DAY_IN_SECONDS);
                    }
                }
                return array('hoster' => 'youtube', 'image' => $image, 'id' => $yid, 'embed' =>
                'https://www.youtube.com/embed/' . $yid);
            }
            // else return id
            else {
                return $yid;
            }
        } else if ($vid) {
            $oembed_endpoint = 'https://vimeo.com/api/oembed';
            $json_url = $oembed_endpoint . '.json?url=' . rawurlencode($url) . '&width=765';
            $response = wp_remote_get($json_url);
            if (!is_wp_error($response) && $response['response']['code'] == 200) {
                $vimeoObject = json_decode($response['body']);
            }
            if (!empty($vimeoObject) && $vimeoObject !== FALSE) {
                //return embed iframe
                if ($return == 'embed') {
                    return '<iframe width="' . ($width ? $width : $vimeoObject['width']) . '"
                    height="' . ($height ? $height : $vimeoObject['height']) . '"
                    src="//player.vimeo.com/video/' . $vid . '?title=0&byline=0&portrait=0" frameborder="0"
                    webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>';
                }
                //return normal thumb
                else if ($return == 'thumb' || $return == 'maxthumb') {
                    if (is_array($vimeoObject)) {
                        return $vimeoObject['thumbnail_url'];
                    }
                } else if ($return == 'embedurl') {
                    return '//player.vimeo.com/video/' . $vid;
                }
                //return medium thumb
                else if ($return == 'thumbmed') {
                    return str_replace('_640', '_340', $vimeoObject['thumbnail_url']);
                }
                //return hqthumb
                else if ($return == 'hqthumb') {
                    return $vimeoObject['thumbnail_url'];
                } else if ($return == 'hoster') {
                    return 'vimeo';
                } else if ($return == 'data') {
                    return array('hoster' => 'vimeo', 'image' => $vimeoObject['thumbnail_url'], 'id' => $vid, 'embed' =>
                    '//player.vimeo.com/video/' . $vid);
                }
                // else return id
                else {
                    return $vid;
                }
            }
        }
    }
}

//////////////////////////////////////////////////////////////////
// ACPT Repeater
//////////////////////////////////////////////////////////////////
function gspb_acptConvertArray($inputArray)
{
    $outputArray = [];

    // Get the keys from the first item in the input array
    $keys = array_keys($inputArray);

    // Loop through the values of the first item in the input array
    for ($i = 0; $i < count($inputArray[$keys[0]]); $i++) {
        $item = [];

        // Loop through the keys and add the corresponding value to the item array
        foreach ($keys as $key) {
            $value = $inputArray[$key][$i]['value'];
            $item[$key] = $value;
        }

        // Add the item to the output array
        $outputArray[] = $item;
    }

    return $outputArray;
}

//////////////////////////////////////////////////////////////////
// API Connector
//////////////////////////////////////////////////////////////////
function gspb_api_connector_callback(WP_REST_Request $request)
{
    $nonce = sanitize_text_field($request->get_param('nonce'));
    if (!wp_verify_nonce($nonce, 'wp_rest')) {
        return new WP_REST_Response('Invalid nonce', 403);
    }

    $api_id = sanitize_text_field($request->get_param('api_id'));
    $runindex = sanitize_text_field($request->get_param('runindex'));
    $formdata = !empty($request->get_param('formdata')) ? greenshiftquery_sanitize_multi_array($request->get_param('formdata')) : [];
    if(!$api_id) return new WP_REST_Response(array(), 200);
    $block = get_transient($api_id);
    if(empty($block)) return new WP_REST_Response(array(), 200);

    $generated_content = GSPB_generate_dynamic_repeater('repeater_generate', $block, $formdata, $runindex);

    if (!empty($generated_content)) {
        return new WP_REST_Response($generated_content, 200);
    } else {
        return new WP_REST_Response(array(), 200);
    }
}
function gspb_api_connector_run( $filters, $runindex = null ) {
    extract($filters);
	$response_data = [];
    $apiMethod = ! empty ( $apiMethod ) ? $apiMethod : 'GET';
    $args = [
		'method'     => $apiMethod,
        'timeout'     => ! empty( $apiTimeout ) ? (int) $apiTimeout : 30,
        'redirection' => 5,
        'sslverify'   => false,
		'headers'     => [
			'Content-Type' => 'application/json',
		],
    ];

    if(!empty($pagination_selector) && isset($runindex)){
        $apiUrl = add_query_arg( [ 'page' => $runindex + 1 ], $apiUrl );
    }

    // Prepare headers
    if ( ! empty( $apiHeaders ) && is_array( $apiHeaders ) ) {
        $args['headers'] = array_reduce( $apiHeaders, function ( $headers, $header ) {
			$header_name  = ( isset( $header['name'] ) ) ? sanitize_text_field( $header['name'] ) : '';
			$header_value = ( isset( $header['value'] ) ) ? sanitize_text_field( $header['value'] ) : '';

            if ( ! empty( $header_name ) && ! empty( $header_value ) ) {
                $headers[ $header_name ] = $header_value;
            }
            return $headers;
        }, []);
    }

    // Process query or body parameters
    if ( ! empty( $apiBody ) ) {
        // If apiBody is a simple text string, just use it directly.
        if ( 'GET' === $apiMethod ) {
            // For GET requests, add the encoded body as a query parameter.
            $encodedBody = gspb_ensure_json_encoded($apiBody);
            $apiUrl = add_query_arg( [ 'body' => $encodedBody ], $apiUrl );
        } else {
            // For non-GET (POST, etc.), set the body, making sure it’s properly encoded.
            $args['body'] = gspb_ensure_json_encoded($apiBody);
        }
    }

    // Execute the API request
    $response = ( 'GET' === $apiMethod ) ? wp_safe_remote_get( $apiUrl, $args ) : wp_safe_remote_post( $apiUrl, $args );

    // Handle response
    if ( is_wp_error( $response ) ) {
        error_log( 'API Request Error: ' . $response->get_error_message() );
    } else {
        $body          = wp_remote_retrieve_body( $response );
        if(!empty($body)){
            $body = json_decode( $body, true );
        }
        if(!empty($responseField) && !empty($body)){
            // Split the string "field[0][message]" into an array like ["field", "0", "message"]
            $keys = preg_split('/[\[\]]+/', $responseField, -1, PREG_SPLIT_NO_EMPTY);
            foreach ($keys as $key) {
                if (isset($body[$key])) {
                    $body = $body[$key];
                } else {
                    // If some key doesn't exist, set $body to null (or handle it as you need).
                    $body = null;
                    break;
                }
            }
        }

        if (is_null($body)) {
            $body = '';
        }
        $response_data = $body;

        if ( json_last_error() !== JSON_ERROR_NONE ) {
            error_log( 'JSON Decode Error: ' . json_last_error_msg() );
        }
    }

    return $response_data;
}

/**
 * Ensure that the passed data is JSON-encoded.
 *
 * @param mixed $data The data to be checked/encoded.
 * @return string JSON-encoded string.
 */
function gspb_ensure_json_encoded($data) {
    // If the data is a string, try to check if it is already valid JSON.
    if (is_string($data)) {
        $trimmed = trim($data);
        // A valid JSON string usually starts with '{' (object) or '[' (array)
        if ((substr($trimmed, 0, 1) === '{' || substr($trimmed, 0, 1) === '[')) {
            json_decode($data);
            if (json_last_error() === JSON_ERROR_NONE) {
                // Already valid JSON.
                return $data;
            }
        }
    }
    // Otherwise, encode the data (works if $data is an array or any non-string data).
    return json_encode($data);
}

function gspb_get_license_settings()
{

	try {
		$licenses = gl_edd_check_all_licenses();
		return array(
			'success'  => true,
			'license' => $licenses
		);
	} catch (Exception $e) {
		return array(
			'success' => false,
			'message' => $e->getMessage(),
		);
	}
}

function gspb_get_global_settings()
{

	try {
		$settings = get_option('gspb_global_settings');
		return array(
			'success'  => true,
			'settings' => $settings
		);
	} catch (Exception $e) {
		return array(
			'success' => false,
			'message' => $e->getMessage(),
		);
	}
}

function gspb_get_public_assets()
{
	try {
		$settings = get_option('gspb_global_settings');
		$assets = [];
		$assets['colours'] = !empty($settings['colours']) ? $settings['colours'] : [];
		$assets['gradients'] = !empty($settings['gradients']) ? $settings['gradients'] : [];
		$assets['localfont'] = !empty($settings['localfont']) ? $settings['localfont'] : [];
		$assets['localfontcss'] = !empty($settings['localfontcss']) ? $settings['localfontcss'] : [];
		$assets['elements'] = !empty($settings['elements']) ? $settings['elements'] : [];

		return array(
			'success'  => true,
			'settings' => $assets,
		);
	} catch (Exception $e) {
		return array(
			'success' => false,
			'message' => $e->getMessage(),
		);
	}
}

function gspb_update_global_settings($request)
{

	try {
		$params = $request->get_params();
		$defaults = get_option('gspb_global_settings');
		$settings = '';

		if ($defaults === false) {
			add_option('gspb_global_settings', $params);
			$settings = $params;
		} else {
			$newargs = wp_parse_args($params, $defaults);
			update_option('gspb_global_settings', $newargs);
			$settings = $newargs;
		}

		if(!empty($params['global_classes'])){
			$default_global_classes = get_option('greenshift_global_classes');
			if ($default_global_classes === false) {
				add_option('greenshift_global_classes', $params['global_classes']);
			} else {
				update_option('greenshift_global_classes', $params['global_classes']);
			}
		}

		$upload_dir = wp_upload_dir();
		require_once ABSPATH . 'wp-admin/includes/file.php';
		global $wp_filesystem;
		$dir = trailingslashit($upload_dir['basedir']) . 'GreenShift/'; // Set storage directory path
		WP_Filesystem(); // WP file system
		if (!$wp_filesystem->is_dir($dir)) {
			$wp_filesystem->mkdir($dir);
		}

		$gspb_json_filename = 'settings_backup.json';
		// Exclude API keys from backup
		$backup_settings = $settings;
		$keys_to_exclude = [
			'googleapi',
			'turnstile_site_key',
			'turnstile_secret_key',
			'openaiapi',
			'claudeapi',
			'deepseekapi',
			'geminiapi'
		];
		foreach ($keys_to_exclude as $key) {
			if (isset($backup_settings[$key])) {
				unset($backup_settings[$key]);
			}
		}

		$gspb_backup_data = json_encode( $backup_settings, JSON_PRETTY_PRINT );

		if (!$wp_filesystem->put_contents($dir . $gspb_json_filename, $gspb_backup_data)) {
			throw new Exception(__('JSON is not saved due the permission!!!', 'gl-page-builder'));
		}

		if(!empty($params['figma_fonts'])){
			$figma_fonts = $params['figma_fonts'];
			$localfont = !empty($defaults['localfont']) ? json_decode($defaults['localfont'], true) : [];
			$localfontcss = !empty($defaults['localfontcss']) ? $defaults['localfontcss'] : '';
			$newfonts = false;

			foreach($figma_fonts as $key=>$value){
				$fontName = !empty($value['fontFamily']) ? $value['fontFamily'] : '';
				if(!$fontName || in_array($fontName, $localfont)){
					continue;
				}
				$fontFile = !empty($value['fontFile']) ? $value['fontFile'] : '';
				if(!$fontFile){
					continue;
				}
				$fontExtension = pathinfo($value['fontFile'], PATHINFO_EXTENSION);
				$allowed_font_ext = [
					'woff2',
					'woff',
					'tiff',
					'ttf',
				]; 
				if(!$fontExtension || !in_array($fontExtension, $allowed_font_ext)){
					continue;
				}


				$newfilename = basename($fontFile);
				$ext = $fontExtension;
		
				if ($newfilename = greenshift_download_file_localy($fontFile, $dir, $newfilename, $ext)) {
					$fonturl = trailingslashit($upload_dir['url']) .$newfilename;
					$localfont[$fontName] = [
						'woff2' => ($fontExtension == 'woff2') ? $fonturl : '',
						'woff' => ($fontExtension == 'woff') ? $fonturl : '',
						'tiff' => ($fontExtension == 'tiff') ? $fonturl : '',
						'ttf' => ($fontExtension == 'ttf') ? $fonturl : '',
					];

					$localfontcss .= '@font-face {';
						$localfontcss .= 'font-family: "' . $fontName . '";';
						$localfontcss .= 'src: ';
						if ($fontExtension == 'woff2') {
							$localfontcss .= 'url(' . $fonturl . ') format("woff2"), ';
						}
						if ($fontExtension == 'woff') {
							$localfontcss .= 'url(' . $fonturl . ') format("woff"), ';
						}
						if ($fontExtension == 'ttf') {
							$localfontcss .= 'url(' . $fonturl . ') format("truetype"), ';
						}
						if ($fontExtension == 'tiff') {
							$localfontcss .= 'url(' . $fonturl . ') format("tiff"), ';
						}
						$localfontcss .= ';';
					$localfontcss .= 'font-display: swap;}';
					$newfonts = true;
				} else {
					continue;
				}

			}

			if($newfonts){
				$localfontcss = str_replace(', ;', ';', $localfontcss);
				$localfont = json_encode($localfont);
				$settings['localfont'] = $localfont;
				$settings['localfontcss'] = $localfontcss;
				update_option('gspb_global_settings', $settings);
			}
			
		}

		if(!empty($params['figma_colors']) && is_array($params['figma_colors'])){
			$figma_colors = $params['figma_colors'];
			$colours = !empty($defaults['colours']) ? $defaults['colours'] : [];

			foreach($figma_colors as $key=>$value){
				$colours[$key] = $value;
			}

			$colours = json_encode($colours);
			$settings['colours'] = $colours;
			update_option('gspb_global_settings', $settings);
		}

		if(!empty($params['figma_classes']) && is_array($params['figma_classes'])){
			$figma_classes = $params['figma_classes'];
			$classes = !empty($defaults['global_classes']) ? $defaults['global_classes'] : [];
			$classes_values = array_column($classes, 'value');

			foreach($figma_classes as $key=>$value){
				if(!empty($value['value'])){
					$index = array_search($value['value'], $classes_values);
					if($index){
						$classes[$index] = $value;
					}else{
						$classes[] = $value;
					}
				}
			}
			$defaults['global_classes'] = $classes;
			update_option('gspb_global_settings', $defaults);
		}

		if(isset($params['custom_css'])){
			$defaults['custom_css'] = $params['custom_css'];
			update_option('gspb_global_settings', $defaults);
		}

		if(isset($params['framework_classes'])){
			if(!is_array($params['framework_classes'])){
				$params['framework_classes'] = [];
			}
			$defaults['framework_classes'] = $params['framework_classes'];
			update_option('gspb_global_settings', $defaults);
		}

		if(!empty($params['figma_gradients']) && is_array($params['figma_gradients'])){
			$figma_gradients = $params['figma_gradients'];
			$gradients = !empty($defaults['gradients']) ? $defaults['gradients'] : [];

			foreach($figma_gradients as $key=>$value){
				$gradients[$key] = $value;
			}
			$gradients = json_encode($gradients);
			$settings['gradients'] = $gradients;
			update_option('gspb_global_settings', $settings);
		}

		if(!empty($params['figma_elements']) && is_array($params['figma_elements'])){
			$figma_elements = $params['figma_elements'];
			$elements = json_encode($figma_elements);
			$settings['elements'] = $elements;
			update_option('gspb_global_settings', $settings);
		}

		return json_encode(array(
			'success' => true,
			'message' => 'Global settings updated!',
		));
	} catch (Exception $e) {
		return json_encode(array(
			'success' => false,
			'message' => $e->getMessage(),
		));
	}
}

/**
 * Apply a single add/update/delete operation to the global classes library.
 *
 * The editor used to POST the entire global_classes array on every save, so the
 * request body grew with the library and eventually exceeded managed-host WAF
 * limits (silent 403s on Flywheel, WP Engine, Kinsta, etc.). This endpoint takes
 * just one class plus an operation, applies it server-side, and keeps both the
 * gspb_global_settings['global_classes'] store and the standalone
 * greenshift_global_classes mirror in sync.
 *
 * Expected params:
 *   operation  : 'add' | 'update' | 'delete'
 *   value      : unique class identifier (matched against each class 'value')
 *   class      : single class object (required for add/update)
 *   interaction / animation       : optional single entry to set on add/update
 *                                   (null removes the entry for this class)
 *   remove_interaction / remove_animation : truthy to drop the entry on delete
 */
function gspb_update_global_class($request)
{
	try {
		$params    = $request->get_params();
		$operation = !empty($params['operation']) ? sanitize_text_field($params['operation']) : '';

		if (!in_array($operation, array('add', 'update', 'delete'), true)) {
			throw new Exception('Unknown global class operation.');
		}

		// Resolve the target class identifier (the unique "value" of the class).
		$value = '';
		if (isset($params['value']) && $params['value'] !== '') {
			$value = sanitize_text_field($params['value']);
		} elseif (!empty($params['class']['value'])) {
			$value = sanitize_text_field($params['class']['value']);
		}

		if ($value === '') {
			throw new Exception('No class identifier provided.');
		}

		$settings = get_option('gspb_global_settings');
		if (!is_array($settings)) {
			$settings = array();
		}

		$classes = (!empty($settings['global_classes']) && is_array($settings['global_classes'])) ? $settings['global_classes'] : array();

		// Locate the class by its "value".
		$found_index = -1;
		foreach ($classes as $idx => $cls) {
			if (isset($cls['value']) && $cls['value'] === $value) {
				$found_index = $idx;
				break;
			}
		}

		if ($operation === 'add' || $operation === 'update') {
			if (empty($params['class']) || !is_array($params['class'])) {
				throw new Exception('No class data provided.');
			}

			if ($found_index > -1) {
				$classes[$found_index] = $params['class'];
			} else {
				$classes[] = $params['class'];
			}

			// Optionally set (or clear with null) this class's interaction entry.
			if (array_key_exists('interaction', $params)) {
				if (!isset($settings['global_interactions']) || !is_array($settings['global_interactions'])) {
					$settings['global_interactions'] = array();
				}
				if ($params['interaction'] === null || $params['interaction'] === '') {
					unset($settings['global_interactions'][$value]);
				} else {
					$settings['global_interactions'][$value] = $params['interaction'];
				}
			}

			// Optionally set (or clear with null) this class's animation entry.
			if (array_key_exists('animation', $params)) {
				if (!isset($settings['global_animations']) || !is_array($settings['global_animations'])) {
					$settings['global_animations'] = array();
				}
				if ($params['animation'] === null || $params['animation'] === '') {
					unset($settings['global_animations'][$value]);
				} else {
					$settings['global_animations'][$value] = $params['animation'];
				}
			}
		} elseif ($operation === 'delete') {
			if ($found_index > -1) {
				array_splice($classes, $found_index, 1);
			}
			if (!empty($params['remove_interaction']) && isset($settings['global_interactions'][$value])) {
				unset($settings['global_interactions'][$value]);
			}
			if (!empty($params['remove_animation']) && isset($settings['global_animations'][$value])) {
				unset($settings['global_animations'][$value]);
			}
		}

		$classes                    = array_values($classes);
		$settings['global_classes'] = $classes;
		update_option('gspb_global_settings', $settings);

		// Keep the standalone mirror option (used by abilities / MCP) in sync.
		update_option('greenshift_global_classes', $classes);

		return json_encode(array(
			'success' => true,
			'message' => 'Global class updated!',
		));
	} catch (Exception $e) {
		return json_encode(array(
			'success' => false,
			'message' => $e->getMessage(),
		));
	}
}

function gspb_update_css_settings($request)
{

	try {
		$css = sanitize_text_field($request->get_param('css'));
		$id = sanitize_text_field($request->get_param('id'));
		
		// Security check: Verify the post exists and user has permission to edit it
		if (!$id || !get_post($id)) {
			return json_encode(array(
				'success' => false,
				'message' => 'Post not found.',
			));
		}
		
		// Check if user can edit this specific post
		if (!current_user_can('edit_post', $id)) {
			return json_encode(array(
				'success' => false,
				'message' => 'You do not have permission to edit this post.',
			));
		}
		
		if ($css) {
			update_post_meta($id, '_gspb_post_css', $css);
		} else {
            $current_css = get_post_meta($id, '_gspb_post_css', true);
            if($current_css){
			    delete_post_meta($id, '_gspb_post_css');
            }
		}

		return json_encode(array(
			'success' => true,
			'message' => 'Post css updated!',
		));
	} catch (Exception $e) {
		return json_encode(array(
			'success' => false,
			'message' => $e->getMessage(),
		));
	}
}

function gspb_update_global_wp_settings($request)
{

	try {
		$params = $request->get_params();
		$settings = wp_get_global_settings();
		if(!empty($params['colors'])){
			$colors = [];
			foreach($params['colors'] as $key=>$value){
				$colors[$key] = sanitize_text_field($value['color']);
			}

			$theme = wp_get_theme();
			if ($theme->parent_theme) {
				$template_dir = basename(get_template_directory());
				$theme = wp_get_theme($template_dir);
			}
			$themename = $theme->get('TextDomain');
	
			// Define post parameters
			$post_type = 'wp_global_styles';
			$post_name = 'wp-global-styles-'.$themename;
		
			$stylesObject = get_page_by_path($post_name, OBJECT, $post_type);
			$stylesPostId = is_object($stylesObject) ? $stylesObject->ID : '';
		
			if ($stylesPostId) {
		
				$post_id = $stylesPostId;
				$content = $stylesObject->post_content;
				$contentclean = json_decode($content, true);
				if(empty($contentclean)){
					$contentclean = array();
				}
				if (empty($contentclean['settings']['color']['palette']['theme'])) {
					$contentclean['settings']['color']['palette']['theme'] = $settings['color']['palette']['theme'];
					$contentclean["isGlobalStylesUserThemeJSON"] = true;
					$contentclean["version"] = 3;
				}
				if(!empty($colors)){
					foreach($colors as $key=>$value){
						$contentclean['settings']['color']['palette']['theme'][$key]['color'] = $value;
					}
				}
		
				// Update post data as needed
				$post_data = array(
					'ID'   =>  $stylesPostId,
					'post_name' => $post_name, // Replace with the new slug
					'post_status' => 'publish',
					'post_title' => $stylesObject->post_title, // Replace with the new title
					'post_content' => wp_slash(json_encode($contentclean)), // Replace with the new content
				);
		
				// Update the post
				wp_update_post($post_data);
			} else {
				$contentclean = array();
				$contentclean['settings']['color']['palette']['theme'] = $settings['color']['palette']['theme'];
				$contentclean["isGlobalStylesUserThemeJSON"] = true;
				$contentclean["version"] = 3;
				if(!empty($colors)){
					foreach($colors as $key=>$value){
						$contentclean['settings']['color']['palette']['theme'][$key]['color'] = $value;
					}
				}
				$content = json_encode($contentclean);
				$post_id = wp_insert_post(array(
					'post_name' => $post_name,
					'post_title' => "Custom Styles",
					'post_type' => $post_type,
					'post_content' => $content,
					'post_status' => 'publish',
				));
		
				$category_domain = 'wp_theme';
				$category_slug = $themename;
				$category_slug = str_replace( '"', '', $category_slug );
		
				wp_set_object_terms($post_id, $category_slug, $category_domain, false);
			}
		}

		return json_encode(array(
			'success' => true,
			'message' => 'Settings updated!',
		));
	} catch (Exception $e) {
		return json_encode(array(
			'success' => false,
			'message' => $e->getMessage(),
		));
	}
}

function gspb_convert_svgstring_from_svg_image(WP_REST_Request $request)
{
	$imageid = intval($request->get_param('imageid'));

	$result = '';

	if($imageid){
		$path = wp_get_original_image_path( $imageid );
		require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
		require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
		$filesystem = new WP_Filesystem_Direct( true );

		$content = $filesystem->get_contents($path);
		$result = ['svg' => $content];
	}

	return json_encode($result);

}

function gspb_get_csv_to_json(WP_REST_Request $request)
{
	$url = sanitize_text_field($request->get_param('url'));
	$type = sanitize_text_field($request->get_param('type') ?: 'row');
	$remove_rows = $request->get_param('remove_rows') ? array_map('intval', explode(',', sanitize_text_field($request->get_param('remove_rows')))) : [];
	$remove_columns = $request->get_param('remove_columns') ? array_map('intval', explode(',', sanitize_text_field($request->get_param('remove_columns')))) : [];
	$result = [];
	
	if($url){
		// Check if the URL is URL-encoded and decode it if necessary
		if(urldecode($url) !== $url && filter_var(urldecode($url), FILTER_VALIDATE_URL)) {
			$url = urldecode($url);
		}
		
		// Check if URL is a Google Sheets URL that needs conversion to CSV export format
		if(strpos($url, 'docs.google.com/spreadsheets') !== false) {
			// If it's not already in the CSV output format
			if(strpos($url, 'output=csv') === false) {
				// If it's a standard /d/ format URL
				if(preg_match('/\/d\/(.*?)\//', $url, $matches)) {
					$sheet_id = $matches[1];
					// Convert to export URL (format: CSV)
					$url = 'https://docs.google.com/spreadsheets/d/' . $sheet_id . '/export?format=csv';
				}
				// Otherwise, it might already be in the published format with /e/PACX-... structure
				// which should work directly if output=csv is added
				elseif(strpos($url, '/pub') !== false) {
					// If it's published but missing output format
					$url = add_query_arg('output', 'csv', $url);
				}
			}
			// If already has output=csv, we'll use it as is
		}
		
		$response = wp_safe_remote_get($url);
		
		if(!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
			$csv_content = wp_remote_retrieve_body($response);
			
			// For chart3, we'll always process all rows as data, never as headers
			if($type === 'chart3') {
				$lines = explode("\n", $csv_content);
				$result = [
					'series' => [],
					'labels' => []
				];
				
				// Process each line directly as a data row
				foreach($lines as $line_num => $line) {
					// Skip removed rows - convert from 1-based to 0-based indexing
					$adjusted_remove_rows = array_map(function($row) { return $row - 1; }, $remove_rows);
					if(in_array($line_num, $adjusted_remove_rows)) {
						continue;
					}
					
					$line = trim($line);
					if(empty($line)) continue;
					
					// Parse this line as CSV
					$row = str_getcsv($line);
					
					// Remove specified columns - convert from 1-based to 0-based indexing
					$adjusted_remove_columns = array_map(function($col) { return $col - 1; }, $remove_columns);
					foreach($adjusted_remove_columns as $col_num) {
						if(isset($row[$col_num])) {
							unset($row[$col_num]);
						}
					}
					// Re-index array after removing columns
					$row = array_values($row);
					
					// We need at least 2 columns
					if(count($row) >= 2) {
						// First column is the label
						$result['labels'][] = trim($row[0]);
						
						// Second column is the value - convert to float
						$value = trim($row[1]);
						if(is_string($value)) {
							$value = str_replace(',', '.', $value);
							$value = str_replace(' ', '', $value);
						}
						$result['series'][] = (float)$value;
					}
				}
				
				// Return immediately for chart3
				return $result;
			}
			
			// Regular CSV processing for other types
			$lines = explode("\n", $csv_content);
			$headers = [];
			$data = [];
			
			// Process CSV into array
			foreach($lines as $line_num => $line) {
				if(empty(trim($line))) continue;
				
				// Create adjusted arrays for 1-based to 0-based indexing
				$adjusted_remove_rows = array_map(function($row) { return $row - 1; }, $remove_rows);
				$adjusted_remove_columns = array_map(function($col) { return $col - 1; }, $remove_columns);
				
				// Skip removed rows (except header row if we're keeping headers)
				if($line_num > 0 && in_array($line_num, $adjusted_remove_rows)) {
					continue;
				}
				
				// str_getcsv handles CSV parsing including quoted values with commas
				$row = str_getcsv($line);
				
				if($line_num === 0) {
					// First line contains headers
					// Remove specified columns from headers
					foreach($adjusted_remove_columns as $col_num) {
						if(isset($row[$col_num])) {
							unset($row[$col_num]);
						}
					}
					// Re-index headers array after removing columns
					$row = array_values($row);
					
					$headers = array_map('trim', $row);
				} else {
					// Data rows
					// Remove specified columns
					foreach($adjusted_remove_columns as $col_num) {
						if(isset($row[$col_num])) {
							unset($row[$col_num]);
						}
					}
					// Re-index array after removing columns
					$row = array_values($row);
					
					$item = [];
					foreach($row as $column_num => $cell) {
						if(isset($headers[$column_num])) {
							$item[$headers[$column_num]] = trim($cell);
						}
					}
					if(!empty($item)) {
						$data[] = $item;
					}
				}
			}
			
			// Default row format
			if($type === 'row') {
				$result = $data;
			}
			// Column format - group each column as separate item
			elseif($type === 'column' && !empty($data)) {
				$result = [];
				
				// Create an array for each column starting with the header name
				foreach($headers as $header) {
					$columnData = [$header]; // Start with header as first element
					
					// Add all values from this column
					foreach($data as $row) {
						if(isset($row[$header])) {
							$columnData[] = $row[$header];
						}
					}
					
					$result[] = $columnData;
				}
			}
			// Column with heading format - first column values as keys
			elseif($type === 'column_w_heading' && !empty($data) && !empty($headers)) {
				$result = [];
				$firstColumnHeader = $headers[0]; // Get the first column header
				
				// Create arrays for each column (starting from the second column)
				for($i = 1; $i < count($headers); $i++) {
					$columnHeader = $headers[$i];
					$columnArray = [];
					
					// First item in the array is the column header with its name
					$columnArray[$firstColumnHeader] = $columnHeader;
					
					// For each row, create a key-value pair using first column as key
					foreach($data as $row) {
						if(isset($row[$firstColumnHeader]) && isset($row[$columnHeader])) {
							$key = $row[$firstColumnHeader];
							$value = $row[$columnHeader];
							$columnArray[$key] = $value;
						}
					}
					
					// Add this column's array to the result
					$result[] = $columnArray;
				}
			}
			// Row with heading format - first row (headers) as keys for pairs
			elseif($type === 'row_w_heading' && !empty($data) && !empty($headers)) {
				$result = [];
				
				// Process each row and create an associative array
				foreach($data as $rowIndex => $row) {
					$rowArray = [];
					$firstColumnHeader = $headers[0];
					$rowIdentifier = $row[$firstColumnHeader]; // Use first column value as row identifier
					
					// First pair is the first column header with the row identifier
					$rowArray[$firstColumnHeader] = $rowIdentifier;
					
					// Add all other columns using headers as keys
					for($i = 1; $i < count($headers); $i++) {
						$header = $headers[$i];
						if(isset($row[$header])) {
							$rowArray[$header] = $row[$header];
						}
					}
					
					// Add this row's array to the result
					$result[] = $rowArray;
				}
			}
			// ApexChart format - Years on x-axis, Countries as series
			elseif($type === 'chart1' && !empty($data)) {
				$apexResult = [
					'series' => [],
					'xaxis' => [
						'categories' => []
					]
				];
				
				// Get the first column name (assuming it's something like "Country")
				$firstColumnName = $headers[0];
				
				// Extract years for xaxis categories (all headers except the first which is the country)
				$yearColumns = array_slice($headers, 1);
				$apexResult['xaxis']['categories'] = $yearColumns;
				
				// Create a series for each country
				foreach($data as $row) {
					$countryName = $row[$firstColumnName];
					$countryData = [];
					
					// Get values for each year for this country
					foreach($yearColumns as $year) {
						// Convert string values to float for the chart
						$countryData[] = (float)$row[$year];
					}
					
					$apexResult['series'][] = [
						'name' => $countryName,
						'data' => $countryData
					];
				}
				
				$result = $apexResult;
			}
			// ApexChart format - First column as x-axis, other columns as series
			elseif($type === 'chart2' && !empty($data)) {
				$apexResult = [
					'series' => [],
					'xaxis' => [
						'categories' => []
					]
				];
				
				// First column is used for x-axis categories
				$entityColumn = $headers[0];
				
				// Get all entities for x-axis
				$entities = [];
				foreach($data as $row) {
					if(isset($row[$entityColumn])) {
						$entities[] = $row[$entityColumn];
					}
				}
				
				// Set x-axis categories
				$apexResult['xaxis']['categories'] = $entities;
				
				// Create a series for each additional column (starting from index 1)
				for($i = 1; $i < count($headers); $i++) {
					$seriesName = $headers[$i];
					$seriesData = [];
					
					// Get values for this column for each entity
					foreach($data as $row) {
						if(isset($row[$seriesName])) {
							// Handle any decimal format (comma or point) and convert to float
							$value = $row[$seriesName];
							if(is_string($value)) {
								$value = str_replace(',', '.', $value);
								$value = str_replace(' ', '', $value); // Also remove any spaces
							}
							$seriesData[] = (float)$value;
						} else {
							$seriesData[] = null; // Use null for missing values
						}
					}
					
					$apexResult['series'][] = [
						'name' => $seriesName,
						'data' => $seriesData
					];
				}
				
				$result = $apexResult;
			}
			// Chart type 3 - Simple two-column format for any label-value pair data
			// Outputs a format with separate 'series' and 'labels' arrays
			elseif($type === 'chart3' && !empty($data)) {
				$apexResult = [
					'series' => [],
					'labels' => []
				];
				
				// For two-column data, it could either have headers or not have headers
				// The example format is: "Label,Value" without explicit headers
				// So we need to check if we're dealing with csv data without headers
				
				// If the first column header is numeric or looks like a value, assume no headers
				$hasHeaders = true;
				
				// If there are only 2 columns and the first record looks like "Label,Value" format
				if(count($headers) == 2) {
					// Check if the first row's data matches what looks like a header pattern
					// (this is a heuristic - if first row has label names and not numbers for first column)
					$firstRowKey = isset($data[0][$headers[0]]) ? $data[0][$headers[0]] : '';
					
					// If first column of first row is numeric, likely no headers
					if(is_numeric($firstRowKey)) {
						$hasHeaders = false;
					}
				}
				
				if($hasHeaders) {
					// Get the column names from headers
					$labelColumn = $headers[0];
					$valueColumn = $headers[1];
					
					// Extract labels and values from data
					foreach($data as $row) {
						if(isset($row[$labelColumn])) {
							$apexResult['labels'][] = $row[$labelColumn];
						}
						
						if(isset($row[$valueColumn])) {
							// Handle any decimal format (comma or point) and convert to float
							$value = $row[$valueColumn];
							if(is_string($value)) {
								$value = str_replace(',', '.', $value);
								$value = str_replace(' ', '', $value); // Also remove any spaces
							}
							$apexResult['series'][] = (float)$value;
						}
					}
				} else {
					// No headers - each row is directly label,value
					// In this case, the first item in each row is the label, second is the value
					foreach($data as $row) {
						$keys = array_keys($row);
						if(count($keys) >= 2) {
							$labelKey = $keys[0];
							$valueKey = $keys[1];
							
							$apexResult['labels'][] = $row[$labelKey];
							
							// Handle any decimal format (comma or point) and convert to float
							$value = $row[$valueKey];
							if(is_string($value)) {
								$value = str_replace(',', '.', $value);
								$value = str_replace(' ', '', $value); // Also remove any spaces
							}
							$apexResult['series'][] = (float)$value;
						}
					}
				}
				
				$result = $apexResult;
			}
		}
	}

	// Return the PHP array directly - WordPress REST API will handle JSON encoding
	return apply_filters('gspb_csv_to_array', $result);
}

function gspb_make_proxy_api_request(WP_REST_Request $request)
{
	$nonce = $request->get_header('X-WP-Nonce');
    
    if (!wp_verify_nonce($nonce, 'wp_rest')) {
        return new WP_Error('rest_forbidden', __('Unauthorized Nonce', 'gl-page-builder'), ['status' => 403]);
    }
	$type = sanitize_text_field($request->get_param('type'));
	// Get body parameters from the request
	$body = $request->get_body();
	if (!empty($body)) {
		$body = json_decode($body, true);
	} else {
		$body = array();
	}
	
	// Fallback to get_json_params() if the above method didn't work
	if (empty($body) || !is_array($body)) {
		$body = $request->get_json_params();
		if (empty($body) || !is_array($body)) {
			$body = array();
		}
	}
	$result = null;
	
	if ($type === 'openaicompletion') {
		$api_key = get_option('gspb_global_settings');
		$api_key = $api_key['openaiapi'];
		
		if (empty($api_key)) {
			return new WP_Error('missing_api_key', 'OpenAI API key is missing', array('status' => 400));
		}
		
		$endpoint = 'https://api.openai.com/v1/chat/completions';
		$headers = array(
			'Authorization' => 'Bearer ' . $api_key,
			'Content-Type' => 'application/json'
		);
	} else if($type === 'openairesponse'){
		$api_key = get_option('gspb_global_settings');
		$api_key = $api_key['openaiapi'];
		
		if (empty($api_key)) {
			return new WP_Error('missing_api_key', 'OpenAI API key is missing', array('status' => 400));
		}
		$endpoint = 'https://api.openai.com/v1/responses';
		$headers = array(
			'Authorization' => 'Bearer ' . $api_key,
			'Content-Type' => 'application/json'
		);
	} else if($type === 'media_upload'){
		// Check if user is logged in
		if (!is_user_logged_in()) {
			return new WP_Error('unauthorized', 'User must be logged in to upload files', array('status' => 401));
		}

		// Check if user has upload capabilities
		if (!current_user_can('upload_files')) {
			return new WP_Error('forbidden', 'User does not have permission to upload files', array('status' => 403));
		}

		// Verify if file was uploaded
		if (empty($_FILES['file'])) {
			return new WP_Error('no_file', 'No file was uploaded', array('status' => 400));
		}

		// Get WordPress upload directory
		$upload_dir = wp_upload_dir();
		$custom_dir = $upload_dir['basedir'] . '/api_upload';
		
		// Create custom upload directory if it doesn't exist
		if (!file_exists($custom_dir)) {
			wp_mkdir_p($custom_dir);
			
			// Create .htaccess to prevent directory listing but allow file access
			$htaccess_content = "Options -Indexes\n";
			file_put_contents($custom_dir . '/.htaccess', $htaccess_content);
		}

		// Get file details
		$file = $_FILES['file'];
		$filename = sanitize_file_name($file['name']);
		$tmp_name = $file['tmp_name'];

		// Enhanced security checks
		$allowed_types = array(
			'image/jpeg',
			'image/jpg',
			'image/png',
			'image/gif',
			'image/webp',
			'image/heic',
			'image/heif',
			'application/pdf',
			'application/text',
		);

		// Verify file type using WordPress function
		$filetype = wp_check_filetype($filename);
		$mime_type = !empty($filetype['ext']) ? $filetype['ext'] : '';
		if (!$mime_type || !in_array($filetype['type'], $allowed_types)) {
			return new WP_Error('invalid_file_type', 'File type not allowed', array('status' => 400));
		}

		// Check file size (limit to 10MB)
		$max_size = 10 * 1024 * 1024;
		if ($file['size'] > $max_size) {
			return new WP_Error('file_too_large', 'File size exceeds limit of 10MB', array('status' => 400));
		}

		// Generate unique filename with timestamp
		$file_ext = pathinfo($filename, PATHINFO_EXTENSION);
		$new_filename = sprintf(
			'%s_%s.%s',
			uniqid(),
			time(),
			$file_ext
		);
		$destination = $custom_dir . '/' . $new_filename;

		// Move file to destination
		if (!move_uploaded_file($tmp_name, $destination)) {
			// Clean up on failure
			if (file_exists($tmp_name)) {
				unlink($tmp_name);
			}
			return new WP_Error('upload_failed', 'Failed to upload file', array('status' => 500));
		}

		// Add file to WordPress media library
		$attachment = array(
			'post_mime_type' => $mime_type,
			'post_title' => sanitize_file_name($filename),
			'post_content' => '',
			'post_status' => 'inherit'
		);

		$attach_id = wp_insert_attachment($attachment, $destination);
		if (is_wp_error($attach_id)) {
			// Clean up on failure
			unlink($destination);
			return $attach_id;
		}

		// Generate metadata for the attachment
		require_once(ABSPATH . 'wp-admin/includes/image.php');
		$attach_data = wp_generate_attachment_metadata($attach_id, $destination);
		wp_update_attachment_metadata($attach_id, $attach_data);

		// Return success response with file details
		return array(
			'success' => true,
			'file_url' => wp_get_attachment_url($attach_id),
			'file_path' => $destination,
			'attachment_id' => $attach_id,
			'mime_type' => $mime_type,
			'file_size' => $file['size']
		);
	} else {
		return new WP_Error('invalid_type', 'Invalid API type specified', array('status' => 400));
	}

	// Check if streaming is enabled
	$is_streaming = isset($body['stream']) && $body['stream'] === true;

	if ($is_streaming) {
		// Set proper headers for streaming
		header('Content-Type: text/event-stream');
		header('Cache-Control: no-cache');
		header('Connection: keep-alive');
		header('X-Accel-Buffering: no'); // Important for Nginx
		
		// Ensure output buffering is handled properly
		if (ob_get_level() > 0) {
			ob_end_flush();
		}
		
		// Initialize cURL
		$ch = curl_init($endpoint);
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_HTTPHEADER, [
			'Authorization: Bearer ' . $api_key,
			'Content-Type: application/json'
		]);
		curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
		curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $data) {
			// Process each chunk of data
			echo $data; // Simply forward the data as-is
			
			// Force flush after each chunk
			if (ob_get_level() > 0) {
				ob_flush();
			}
			flush();
			
			return strlen($data);
		});
		
		$response = curl_exec($ch);
		
		if (curl_errno($ch)) {
			echo "data: " . wp_json_encode(['error' => curl_error($ch)]) . "\n\n";
			flush();
		}
		
		curl_close($ch);
		exit;
	} else {
		// Standard non-streaming request
		$response = wp_safe_remote_post($endpoint, array(
			'headers' => $headers,
			'body' => json_encode($body),
			'timeout' => 120
		));
		
		if (is_wp_error($response)) {
			return $response;
		}
		
		$response_code = wp_remote_retrieve_response_code($response);
		$response_body = wp_remote_retrieve_body($response);
		
		if ($response_code !== 200) {
			return new WP_Error(
				'api_error',
				'Error from OpenAI API: ' . $response_body,
				array('status' => $response_code)
			);
		}
		
		$result = json_decode($response_body, true);
	}
	
	return $result;
}