<?php
/**
 * AJAX Functions
 *
 * Process the front-end AJAX actions.
 *
 * @package     Give
 * @subpackage  Functions/AJAX
 * @copyright   Copyright (c) 2016, GiveWP
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
 * @since       1.0
 */

// Exit if accessed directly.
use Give\Helpers\Form\Template as FormTemplateUtils;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Check if AJAX works as expected
 * Note: Do not use this function before init hook.
 *
 * @since  1.0
 *
 * @param bool $force Flag to test ajax by discarding cache result
 *
 * @return bool True if AJAX works, false otherwise
 */
function give_test_ajax_works( $force = false ) {
	// Handle ajax.
	if ( doing_action( 'wp_ajax_nopriv_give_test_ajax' ) ) {
		wp_die( 0, 200 );
	}

	// Check if the Airplane Mode plugin is installed.
	if ( class_exists( 'Airplane_Mode_Core' ) ) {

		$airplane = Airplane_Mode_Core::getInstance();

		if ( method_exists( $airplane, 'enabled' ) ) {

			if ( $airplane->enabled() ) {
				return true;
			}
		} else {

			if ( 'on' === $airplane->check_status() ) {
				return true;
			}
		}
	}

	add_filter( 'block_local_requests', '__return_false' );

	$works = Give_Cache::get( '_give_ajax_works', true );

	if ( ! $works || $force ) {
		$params = [
			'sslverify' => false,
			'timeout'   => 30,
			'body'      => [
				'action' => 'give_test_ajax',
			],
		];

		$ajax = wp_remote_post( give_get_ajax_url(), $params );

		$works = true;

		if ( is_wp_error( $ajax ) ) {

			$works = false;

		} else {

			if ( empty( $ajax['response'] ) ) {
				$works = false;
			}

			if ( empty( $ajax['response']['code'] ) || 200 !== (int) $ajax['response']['code'] ) {
				$works = false;
			}

			if ( empty( $ajax['response']['message'] ) || 'OK' !== $ajax['response']['message'] ) {
				$works = false;
			}

			if ( ! isset( $ajax['body'] ) || 0 !== (int) $ajax['body'] ) {
				$works = false;
			}
		}

		if ( $works ) {
			Give_Cache::set( '_give_ajax_works', '1', DAY_IN_SECONDS, true );
		}
	}

	/**
	 * Filter the output
	 *
	 * @since 1.0
	 */
	return apply_filters( 'give_test_ajax_works', $works );
}

add_action( 'wp_ajax_nopriv_give_test_ajax', 'give_test_ajax_works' );

/**
 * Get AJAX URL
 *
 * @since  1.0
 *
 * @param array $query
 *
 * @return string
 */
function give_get_ajax_url( $query = [] ) {
	$scheme = defined( 'FORCE_SSL_ADMIN' ) && FORCE_SSL_ADMIN ? 'https' : 'admin';

	$current_url = give_get_current_page_url();
	$ajax_url    = admin_url( 'admin-ajax.php', $scheme );

	if ( preg_match( '/^https/', $current_url ) && ! preg_match( '/^https/', $ajax_url ) ) {
		$ajax_url = preg_replace( '/^http/', 'https', $ajax_url );
	}

	if ( ! empty( $query ) ) {
		$ajax_url = add_query_arg( $query, $ajax_url );
	}

	return esc_url_raw( apply_filters( 'give_ajax_url', $ajax_url ) );
}

/**
 * Loads Checkout Login Fields via AJAX
 *
 * @since  1.0
 *
 * @return void
 */
function give_load_checkout_login_fields() {
	/**
	 * Fire when render login fields via ajax.
	 *
	 * @since 1.7
	 */
	do_action( 'give_donation_form_login_fields' );

	give_die();
}

add_action( 'wp_ajax_nopriv_give_checkout_login', 'give_load_checkout_login_fields' );

/**
 * Load Checkout Fields
 *
 * @since  1.3.6
 *
 * @return void
 */
function give_load_checkout_fields() {
	$form_id = isset( $_POST['form_id'] ) ? $_POST['form_id'] : '';

	ob_start();

	/**
	 * Fire to render registration/login form.
	 *
	 * @since 1.7
	 */
	do_action( 'give_donation_form_register_login_fields', $form_id );

	$fields = ob_get_clean();

	wp_send_json(
		[
			'fields' => wp_json_encode( $fields ),
			'submit' => wp_json_encode( give_get_donation_form_submit_button( $form_id ) ),
		]
	);
}

add_action( 'wp_ajax_give_cancel_login', 'give_load_checkout_fields' );
add_action( 'wp_ajax_nopriv_give_cancel_login', 'give_load_checkout_fields' );
add_action( 'wp_ajax_nopriv_give_checkout_register', 'give_load_checkout_fields' );


/**
 * Retrieve a states drop down
 *
 * @since 2.30.0 add 'state_label' & 'states' to response
 * @since  1.0
 *
 * @return void
 */
function give_ajax_get_states_field() {
    $states_found = false;
    $show_field = true;
    $states_require = true;
    // Get the Country code from the $_POST.
    $country = sanitize_text_field($_POST['country']);

    // Get the field name from the $_POST.
    $field_name = sanitize_text_field($_POST['field_name']);

    $label = __('State', 'give');
    $states_label = give_get_states_label();

    $default_state = '';
    if (give_get_country() === $country) {
        $default_state = give_get_state();
    }

    // Check if $country code exists in the array key for states label.
    if (array_key_exists($country, $states_label)) {
        $label = $states_label[$country];
    }

    if (empty($country)) {
        $country = give_get_country();
    }

    $states = give_get_states($country);
    if (!empty($states)) {
        $args = [
            'name' => $field_name,
            'id' => $field_name,
            'class' => $field_name . '  give-select',
            'options' => $states,
            'show_option_all' => false,
            'show_option_none' => false,
            'placeholder' => $label,
            'selected' => $default_state,
            'autocomplete' => 'address-level1',
        ];
        $data = Give()->html->select($args);
        $states_found = true;
    } else {
        $data = 'nostates';

        // Get the country list that does not have any states init.
        $no_states_country = give_no_states_country_list();

        // Check if $country code exists in the array key.
        if (array_key_exists($country, $no_states_country)) {
            $show_field = false;
        }

        // Get the country list that does not require states.
        $states_not_required_country_list = give_states_not_required_country_list();

        // Check if $country code exists in the array key.
        if (array_key_exists($country, $states_not_required_country_list)) {
            $states_require = false;
        }
    }

    $response = [
        'success' => true,
        'states_found' => $states_found,
        'states_label' => $label,
        'show_field' => $show_field,
        'states_require' => $states_require,
        'data' => $data,
        'default_state' => $default_state,
        'city_require' => !array_key_exists($country, give_city_not_required_country_list()),
        'zip_require' => !array_key_exists($country, give_get_country_list_without_postcodes()),
        'state_label' => $label,
        'states' => array_map(static function ($state) {
            return html_entity_decode($state, ENT_QUOTES);
        }, $states),
    ];
    wp_send_json($response);
}

add_action( 'wp_ajax_give_get_states', 'give_ajax_get_states_field' );
add_action( 'wp_ajax_nopriv_give_get_states', 'give_ajax_get_states_field' );

/**
 * Retrieve donation forms via AJAX for chosen dropdown search field.
 *
 * @since  1.0
 *
 * @return void
 */
function give_ajax_form_search() {
	$results = [];
	$search  = esc_sql( sanitize_text_field( $_POST['s'] ) );

	$args = [
		'post_type'              => 'give_forms',
		's'                      => $search,
		'update_post_term_cache' => false,
		'update_post_meta_cache' => false,
		'cache_results'          => false,
		'no_found_rows'          => true,
		'post_status'            => 'publish',
		'orderby'                => 'title',
		'order'                  => 'ASC',
		'posts_per_page'         => empty( $search ) ? 30 : -1,
	];

	/**
	 * Filter to modify Ajax form search args
	 *
	 * @since 2.1
	 *
	 * @param array $args Query argument for WP_query
	 *
	 * @return array $args Query argument for WP_query
	 */
	$args = (array) apply_filters( 'give_ajax_form_search_args', $args );

	// get all the donation form.
	$query = new WP_Query( $args );
	if ( $query->have_posts() ) {
		while ( $query->have_posts() ) {
			$query->the_post();
			global $post;

			$results[] = [
				'id'   => $post->ID,
				'name' => $post->post_title,
			];
		}
		wp_reset_postdata();
	}

	/**
	 * Filter to modify Ajax form search result
	 *
	 * @since 2.1
	 *
	 * @param array $results Contain the Donation Form id
	 *
	 * @return array $results Contain the Donation Form id
	 */
	$results = (array) apply_filters( 'give_ajax_form_search_response', $results );

	wp_send_json( $results );
}

add_action( 'wp_ajax_give_form_search', 'give_ajax_form_search' );
add_action( 'wp_ajax_nopriv_give_form_search', 'give_ajax_form_search' );

/**
 * Search the donors database via Ajax
 *
 * @since  1.0
 *
 * @return void
 */
function give_ajax_donor_search() {
	global $wpdb;

	$search  = esc_sql( sanitize_text_field( $_POST['s'] ) );
	$results = [];
	if ( ! current_user_can( 'view_give_reports' ) ) {
		$donors = [];
	} else {
		$donors = $wpdb->get_results( "SELECT id,name,email FROM $wpdb->donors WHERE `name` LIKE '%$search%' OR `email` LIKE '%$search%' LIMIT 50" );
	}

	if ( $donors ) {
		foreach ( $donors as $donor ) {

			$results[] = [
				'id'   => $donor->id,
				'name' => $donor->name . ' (' . $donor->email . ')',
			];
		}
	}

	wp_send_json( $results );
}

add_action( 'wp_ajax_give_donor_search', 'give_ajax_donor_search' );


/**
 * Searches for users via ajax and returns a list of results
 *
 * @since  1.0
 *
 * @return void
 */
function give_ajax_search_users() {
	$results = [];

	if ( current_user_can( 'manage_give_settings' ) ) {

		$search = esc_sql( sanitize_text_field( $_POST['s'] ) );

		$get_users_args = [
			'number' => 9999,
			'search' => $search . '*',
		];

		$get_users_args = apply_filters( 'give_search_users_args', $get_users_args );

		$found_users = apply_filters( 'give_ajax_found_users', get_users( $get_users_args ), $search );
		$results     = [];

		if ( $found_users ) {

			foreach ( $found_users as $user ) {

				$results[] = [
					'id'   => $user->ID,
					'name' => esc_html( $user->user_login . ' (' . $user->user_email . ')' ),
				];
			}
		}
	}// End if().

	wp_send_json( $results );

}

add_action( 'wp_ajax_give_user_search', 'give_ajax_search_users' );


/**
 * Queries page by title and returns page ID and title in JSON format.
 *
 * Note: this function in for internal use.
 *
 * @since 2.1
 *
 * @return string
 */
function give_ajax_pages_search() {
	$data = [];
	$args = [
		'post_type' => 'page',
		's'         => give_clean( $_POST['s'] ),
	];

	$query = new WP_Query( $args );

	// Query posts by title.
	if ( $query->have_posts() ) {
		while ( $query->have_posts() ) {
			$query->the_post();

			$data[] = [
				'id'   => get_the_ID(),
				'name' => get_the_title(),
			];
		}
	}

	wp_send_json( $data );
}

add_action( 'wp_ajax_give_pages_search', 'give_ajax_pages_search' );

/**
 * Retrieve Categories via AJAX for chosen dropdown search field.
 *
 * @since  2.1
 *
 * @return void
 */
function give_ajax_categories_search() {
	$results = [];

	/**
	 * Filter to modify Ajax tags search args
	 *
	 * @since 2.1
	 *
	 * @param array $args argument for get_terms
	 *
	 * @return array $args argument for get_terms
	 */
	$args = (array) apply_filters(
		'give_forms_categories_dropdown_args',
		[
			'number'     => 30,
			'name__like' => esc_sql( sanitize_text_field( $_POST['s'] ) ),
		]
	);

	$categories = get_terms( 'give_forms_category', $args );

	foreach ( $categories as $category ) {
		$results[] = [
			'id'   => $category->term_id,
			'name' => $category->name,
		];
	}

	/**
	 * Filter to modify Ajax tags search result
	 *
	 * @since 2.1
	 *
	 * @param array $results Contain the categories id and name
	 *
	 * @return array $results Contain the categories id and name
	 */
	$results = (array) apply_filters( 'give_forms_categories_dropdown_responce', $results );

	wp_send_json( $results );
}

add_action( 'wp_ajax_give_categories_search', 'give_ajax_categories_search' );

/**
 * Retrieve Tags via AJAX for chosen dropdown search field.
 *
 * @since  2.1
 *
 * @return void
 */
function give_ajax_tags_search() {
	$results = [];

	/**
	 * Filter to modify Ajax tags search args
	 *
	 * @since 2.1
	 *
	 * @param array $args argument for get_terms
	 *
	 * @return array $args argument for get_terms
	 */
	$args = (array) apply_filters(
		'give_forms_tags_dropdown_args',
		[
			'number'     => 30,
			'name__like' => esc_sql( sanitize_text_field( $_POST['s'] ) ),
		]
	);

	$categories = get_terms( 'give_forms_tag', $args );

	foreach ( $categories as $category ) {
		$results[] = [
			'id'   => $category->term_id,
			'name' => $category->name,
		];
	}

	/**
	 * Filter to modify Ajax tags search result
	 *
	 * @since 2.1
	 *
	 * @param array $results Contain the tags id and name
	 *
	 * @return array $results Contain the tags id and name
	 */
	$results = (array) apply_filters( 'give_forms_tags_dropdown_responce', $results );

	wp_send_json( $results );
}

add_action( 'wp_ajax_give_tags_search', 'give_ajax_tags_search' );

/**
 * Check for Price Variations (Multi-level donation forms)
 *
 * @since  1.5
 *
 * @return void
 */
function give_check_for_form_price_variations() {

	if ( ! current_user_can( 'edit_give_forms', get_current_user_id() ) ) {
		die( '-1' );
	}

	$form_id = absint( $_POST['form_id'] );
	$form    = get_post( $form_id );

	if ( 'give_forms' !== $form->post_type ) {
		die( '-2' );
	}

	if ( give_has_variable_prices( $form_id ) ) {
		$variable_prices = give_get_variable_prices( $form_id );

		if ( $variable_prices ) {
			$ajax_response = '<select class="give_price_options_select give-select give-select" name="give_price_option">';

			if ( isset( $_POST['all_prices'] ) ) {
				$ajax_response .= '<option value="all">' . esc_html__( 'All Levels', 'give' ) . '</option>';
			}

			foreach ( $variable_prices as $key => $price ) {

				$level_text = ! empty( $price['_give_text'] ) ? esc_html( $price['_give_text'] ) : give_currency_filter( give_format_amount( $price['_give_amount'], [ 'sanitize' => false ] ) );

				$ajax_response .= '<option value="' . esc_attr( $price['_give_id']['level_id'] ) . '">' . $level_text . '</option>';
			}
			$ajax_response .= '</select>';
			echo $ajax_response;
		}
	}

	give_die();
}

add_action( 'wp_ajax_give_check_for_form_price_variations', 'give_check_for_form_price_variations' );


/**
 * Check for Variation Prices HTML  (Multi-level donation forms)
 *
 * @since  1.6
 *
 * @return void
 */
function give_check_for_form_price_variations_html() {
	if ( ! current_user_can( 'edit_give_payments', get_current_user_id() ) ) {
		wp_die();
	}

	$form_id    = ! empty( $_POST['form_id'] ) ? absint( $_POST['form_id'] ) : false;
	$payment_id = ! empty( $_POST['payment_id'] ) ? absint( $_POST['payment_id'] ) : false;
	if ( empty( $form_id ) || empty( $payment_id ) ) {
		wp_die();
	}

	$form = get_post( $form_id );
	if ( ! empty( $form->post_type ) && 'give_forms' !== $form->post_type ) {
		wp_die();
	}

	if ( ! give_has_variable_prices( $form_id ) || ! $form_id ) {
		esc_html_e( 'n/a', 'give' );
	} else {
		$prices_atts = [];
		if ( $variable_prices = give_get_variable_prices( $form_id ) ) {
			foreach ( $variable_prices as $variable_price ) {
				$prices_atts[ $variable_price['_give_id']['level_id'] ] = give_format_amount( $variable_price['_give_amount'], [ 'sanitize' => false ] );
			}
		}

		// Variable price dropdown options.
		$variable_price_dropdown_option = [
			'id'               => $form_id,
			'name'             => 'give-variable-price',
			'chosen'           => true,
			'show_option_all'  => '',
			'show_option_none' => '',
			'select_atts'      => 'data-prices=' . esc_attr( json_encode( $prices_atts ) ),
		];

		if ( $payment_id ) {
			// Payment object.
			$payment = new Give_Payment( $payment_id );

			// Payment meta.
			$payment_meta                               = $payment->get_meta();
			$variable_price_dropdown_option['selected'] = $payment_meta['price_id'];
		}

		// Render variable prices select tag html.
		give_get_form_variable_price_dropdown( $variable_price_dropdown_option, true );
	}

	give_die();
}

add_action( 'wp_ajax_give_check_for_form_price_variations_html', 'give_check_for_form_price_variations_html' );

/**
 * Send Confirmation Email For Complete Donation History Access.
 *
 * @since 4.16.1 Always return a uniform success response regardless of donor existence or throttle state.
 * @since 1.8.17
 *
 * @return bool
 */
function give_confirm_email_for_donation_access() {

	// Verify Security using Nonce.
	if ( ! check_ajax_referer( 'give_ajax_nonce', 'nonce' ) ) {
		return false;
	}

	// Bail Out, if email is empty.
	if ( empty( $_POST['email'] ) ) {
		return false;
	}

	$donor = Give()->donors->get_donor_by( 'email', give_clean( $_POST['email'] ) );
	if ( is_object( $donor ) && Give()->email_access->can_send_email( $donor->id ) ) {
		Give()->email_access->send_email( $donor->id, $donor->email );
	}

	$return            = [];
	$return['status']  = 'success';

	/**
	 * Filter to modify access mail send notice
	 *
	 * @since 2.1.3
	 *
	 * @param string Send notice message for email access.
	 *
	 * @return  string $message Send notice message for email access.
	 */
	$message = (string) apply_filters( 'give_email_access_mail_send_notice', __( 'Please check your email and click on the link to access your complete donation history.', 'give' ) );

	$return['message'] = Give_Notices::print_frontend_notice(
		$message,
		false,
		'success'
	);

	echo json_encode( $return );
	give_die();
}

add_action( 'wp_ajax_nopriv_give_confirm_email_for_donations_access', 'give_confirm_email_for_donation_access' );

/**
 * Render receipt by ajax
 * Note: only for internal use
 *
 * @since 4.9.0 rename function - PHP 8 compatibility
 * @since 2.2.0
 */
function give_get_receipt() {

	$get_data = give_clean( filter_input_array( INPUT_GET ) );

	if ( ! isset( $get_data['shortcode_atts'] ) ) {
		give_die();
	}

	$atts = (array) json_decode( $get_data['shortcode_atts'] );
	$data = give_receipt_shortcode( $atts );

	wp_send_json( $data );
}
add_action( 'wp_ajax_get_receipt', 'give_get_receipt');
add_action( 'wp_ajax_nopriv_get_receipt', 'give_get_receipt');

/**
 * Get ajax url to render content from other website into thickbox
 * Note: only for internal use
 *
 * @param array $args
 *
 * @return string
 * @since 2.5.0
 */
function give_modal_ajax_url( $args = [] ) {
	$args = wp_parse_args(
		$args,
		[
			'action'   => 'give_get_content_by_ajax',
			'_wpnonce' => wp_create_nonce( 'give_get_content_by_ajax' ),
		]
	);

	return esc_url_raw( add_query_arg( $args, admin_url( '/admin-ajax.php' ) ) );
}


/**
 * Return content from url
 * Note: only for internal use
 *
 * @todo use get_version endpoint to read changelog or cache add-ons infro from update_plugins option
 *
 * @return string
 * @since 2.5.0
 */
function give_get_content_by_ajax_handler() {
	check_admin_referer( 'give_get_content_by_ajax' );

	if ( empty( $_GET['url'] ) ) {
		die();
	}

    /**
     * Restrict requests to GiveWP.com plugin readme.txt file only.
     * @link https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
     *
     * @since 2.25.2
     */
    if(! preg_match('^https://givewp.com/downloads/plugins/(.*)/readme.txt$^', $_GET['url'])) {
        die();
    }

	// Handle changelog render request.
	if (
		! empty( $_GET['show_changelog'] )
		&& (int) give_clean( $_GET['show_changelog'] )
	) {
		$msg = __( 'Sorry, unable to load changelog.', 'give' );
		$url = urldecode_deep( give_clean( $_GET['url'] ) );

		$response = wp_remote_get( $url );

		if ( is_wp_error( $response ) ) {
			echo "$msg<br><br><code>Error: {$response->get_error_message()}</code>";
			exit;
		}

		$response = wp_remote_retrieve_body( $response );

		if ( false === strpos( $response, '== Changelog ==' ) ) {
			echo $msg;
			exit;
		}

		$changelog = explode( '== Changelog ==', $response );
		$changelog = end( $changelog );

		echo give_get_format_md( $changelog );
	}

	do_action( 'give_get_content_by_ajax_handler' );

	exit;
}

add_action( 'wp_ajax_give_get_content_by_ajax', 'give_get_content_by_ajax_handler' );


/**
 * Get form template for ajax request.
 *
 * Note: only for internal use
 *
 * @since 2.7.0
 */
function give_get_form_template_id() {
	check_ajax_referer( 'give-donation-form-widget', 'security' );

	$formId = isset( $_POST['formId'] ) ? absint( $_POST['formId'] ) : 0;

	// Send error response if form id does not mentioned.
	if ( ! $formId ) {
		wp_send_json_error();
	}

	$templateID = FormTemplateUtils::getActiveID( $formId );
	$templateID = $templateID ?: 'legacy';

	wp_send_json_success( $templateID );
}
add_action( 'wp_ajax_give_get_form_template_id', 'give_get_form_template_id' );
add_action( 'wp_ajax_no_priv_give_get_form_template_id', 'give_get_form_template_id' );
