<?php
/**
 * Analytics module.
 *
 * @since      1.0
 * @package    RankMathPro
 * @subpackage RankMathPro
 * @author     Rank Math <support@rankmath.com>
 */

namespace RankMathPro\Analytics;

use RankMath\KB;
use RankMath\Helper;
use RankMath\Helpers\Param;
use RankMath\Helpers\DB as DB_Helper;
use RankMath\Traits\Hooker;
use RankMath\Analytics\Stats;

// Analytics.
use RankMathPro\Google\Adsense;
use RankMath\Google\Permissions;
use RankMath\Google\Authentication;
use RankMath\Admin\Admin_Helper;
use RankMathPro\Analytics\Workflow\Jobs;
use RankMathPro\Analytics\Workflow\Workflow;
use RankMathPro\Admin\Admin_Helper as ProAdminHelper;
use RankMathPro\Analytics\DB;
use RankMath\Google\Analytics as GoogleAnalytics;
use RankMath\Analytics\Email_Reports as Email_Reports_Base;

defined( 'ABSPATH' ) || exit;

/**
 * Analytics class.
 */
class Analytics {

	use Hooker;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->action( 'rank_math/admin/enqueue_scripts', 'enqueue_analytics' );
		$this->action( 'rank_math/analytics/options/console', 'add_country_dropdown3' );
		$this->action( 'rank_math/analytics/options/analytics', 'add_country_dropdown2' );
		$this->action( 'update_option_rank_math_analytics_last_updated', 'send_summary' );
		$this->action( 'rank_math/admin/settings/analytics', 'add_new_settings' );
		$this->filter( 'rank_math/analytics/schedule_gap', 'schedule_gap' );
		$this->filter( 'rank_math/analytics/max_days_allowed', 'data_retention_period' );
		$this->filter( 'rank_math/analytics/options/cache_control/description', 'change_description' );
		$this->filter( 'rank_math/analytics/check_all_services', 'check_all_services' );
		$this->filter( 'rank_math/analytics/user_preference', 'change_user_preference' );
		$this->action( 'template_redirect', 'local_js_endpoint' );
		$this->filter( 'rank_math/analytics/gtag_config', 'gtag_config' );
		$this->filter( 'rank_math/status/rank_math_info', 'google_permission_info' );
		$this->filter( 'rank_math/analytics/gtag', 'gtag' );
		$this->filter( 'rank_math/analytics/pre_filter_data', 'filter_winning_losing_posts', 10, 3 );
		$this->filter( 'rank_math/analytics/pre_filter_data', 'filter_winning_keywords', 10, 3 );
		$this->action( 'cmb2_save_options-page_fields_rank-math-options-general_options', 'sync_global_settings', 25, 2 );
		$this->action( 'rank_math/settings/before_save', 'before_settings_save', 25, 2 );
		$this->filter( 'rank_math/metabox/post/values', 'add_metadata', 10, 2 );
		$this->filter( 'rank_math/analytics/date_exists_tables', 'date_exists_tables', 10 );
		$this->filter( 'rank_math/analytics/rows', 'add_analytics_rows', 10, 3 );
		$this->filter( 'rank_math/analytics/adsense', 'adsense_options', 10, 3 );
		$this->filter( 'rank_math/analytics/options/data', 'options_data' );
		$this->filter( 'rank_math/cache/generate_hash', 'generate_hash' );

		if ( Helper::has_cap( 'analytics' ) ) {
			$this->action( 'rank_math/admin_bar/items', 'admin_bar_items', 11 );
			$this->action( 'rank_math_seo_details', 'post_column_search_traffic' );
		}

		if ( Helper::can_add_frontend_stats() ) {
			$this->action( 'wp_enqueue_scripts', 'enqueue' );
		}

		Posts::get();
		Keywords::get();
		Jobs::get();
		Workflow::get();
		new Pageviews();
		new Summary();
		new Ajax();
		new Email_Reports();
		new Url_Inspection();
	}

	/**
	 * Add options data.
	 *
	 * @param array $data Data to be added.
	 */
	public function options_data( $data ) {
		$registered = Admin_Helper::get_registration_data();

		$data['analytics']['console_email_frequency']         = Helper::get_settings( 'general.console_email_frequency' );
		$data['analytics']['console_email_send_to']           = Helper::get_settings( 'general.console_email_send_to', $registered['email'] ?? '' );
		$data['analytics']['console_email_heading']           = Helper::get_settings( 'general.console_email_heading', 'SEO Report of Your Website' );
		$data['analytics']['console_email_subject']           = Helper::get_settings( 'general.console_email_subject', Email_Reports::get()->get_subject_default() );
		$data['analytics']['console_email_logo']              = Helper::get_settings( 'general.console_email_logo', Email_Reports::get()->get_logo_url_default() );
		$data['analytics']['console_email_logo_link']         = KB::get( 'email-reports-logo', 'PRO Email Report Logo' );
		$data['analytics']['console_email_header_background'] = Helper::get_settings( 'general.console_email_header_background', Email_Reports::get()->get_header_bg_default() );
		$data['analytics']['console_email_link_full_report']  = Helper::get_settings( 'general.console_email_link_full_report', true );
		$data['analytics']['console_email_sections']          = Helper::get_settings( 'general.console_email_sections', [ 'summary', 'positions', 'winning_posts', 'winning_keywords', 'losing_keywords' ] );
		$data['analytics']['console_email_footer_text']       = Helper::get_settings( 'general.console_email_footer_text', Email_Reports::get()->get_default_footer_text() );

		// Extra fields.
		$data['hideEmailReportOptions'] = Email_Reports_Base::are_fields_hidden();

		return $data;
	}

	/**
	 * Add AdSense markup
	 *
	 * @param string $html      HTML markup.
	 * @param array  $analytics Analytics data.
	 * @param array  $all_services All services data.
	 */
	public function adsense_options( $html, $analytics, $all_services ) {
		$adsense_id           = $analytics['adsense_id'] ?? '';
		$is_adsense_connected = ! empty( $adsense_id );
		$is_valid_connection  = Adsense::is_valid_connection();

		$adsense_classes        = Helper::classnames(
			'rank-math-box no-padding rank-math-accordion rank-math-connect-adsense',
			[
				'connected'    => $is_valid_connection,
				'disconnected' => ! $is_valid_connection,
				'disabled'     => ! Permissions::has_adsense(),
			]
		);
		$adsense_status_classes = Helper::classnames(
			'rank-math-connection-status',
			[
				'rank-math-connection-status-success' => Permissions::has_adsense() && $is_valid_connection,
				'rank-math-connection-status-error'   => Permissions::has_adsense() && ! $is_valid_connection,
			]
		);
		$adsense_status         = $is_valid_connection ? 'Connected' : 'Not Connected';
		ob_start();
		?>
		<div class="<?php echo esc_attr( $adsense_classes ); ?>" tabindex="0">
			<header>
				<h3><span class="rank-math-connection-status-wrap"><span class="<?php echo esc_attr( $adsense_status_classes ); ?>" title="<?php echo esc_attr( $adsense_status ); ?>"></span></span><?php esc_html_e( 'AdSense', 'rank-math-pro' ); ?></h3>
			</header>
			<div class="rank-math-accordion-content">
		
				<?php
				if ( ! Permissions::has_adsense() ) {
					Permissions::print_warning();
				}
				?>
		
				<div class="cmb-row cmb-type-select">
					<div class="cmb-row-col">
						<label for="site-adsense-account"><?php esc_html_e( 'Account', 'rank-math-pro' ); ?></label>
						<select class="cmb2_select site-adsense-account notrack" name="site-adsense-account" id="site-adsense-account" data-selected="<?php echo esc_attr( $adsense_id ); ?>" disabled="disabled">
							<option value=""><?php esc_html_e( 'Select Account', 'rank-math-pro' ); ?></option>
							<?php
							if ( $is_adsense_connected ) :
								$adsense = $all_services['adsenseAccounts'][ $adsense_id ];
								?>
							<option value="<?php echo esc_attr( $adsense_id ); ?>" <?php selected( $adsense_id, 'accounts/' . $adsense['name'], true ); ?>><?php echo esc_html( $adsense['name'] ); ?></option>
							<?php endif; ?>
						</select>
					</div>
				</div>

				<?php if ( ! Adsense::is_valid_connection() ) : ?>
					<div class="rank-math-notice rank-math-notice--error">
						<p><?php esc_html_e( 'Data import will not work for this service as sufficient permissions are not given.', 'rank-math-pro' ); ?></p>
					</div>
				<?php endif; ?>

			</div>
		</div>
		<?php
		return ob_get_clean();
	}

	/**
	 * Add analytics rows.
	 *
	 * @param array $rows     Analytics data rows.
	 * @param array $args     Query arguments.
	 * @param array $page_urls Page URLs.
	 *
	 * @return array
	 */
	public function add_analytics_rows( $rows, $args, $page_urls ) {
		if ( ! GoogleAnalytics::is_analytics_connected() || ! $args['pageview'] || empty( $page_urls ) ) {
			return $rows;
		}

		$pageviews = Pageviews::get_pageviews( [ 'pages' => $page_urls ] );
		if ( empty( $pageviews['rows'] ) ) {
			return $rows;
		}

		foreach ( $pageviews['rows'] as $row ) {
			$page = $row['page'];
			if ( ! isset( $rows[ $page ] ) ) {
				$rows[ $page ] = [];
			}

			$rows[ $page ]['pageviews'] = [
				'total'      => (int) $row['pageviews'],
				'difference' => (int) $row['difference'],
			];
		}

		return $rows;
	}

	/**
	 * Enqueue Frontend stats script.
	 */
	public function enqueue() {
		if ( ! is_singular() || is_admin() || is_preview() || Helper::is_divi_frontend_editor() ) {
			return;
		}

		$uri = untrailingslashit( plugin_dir_url( __FILE__ ) );
		wp_enqueue_style( 'rank-math-analytics-pro-stats', $uri . '/assets/css/admin-bar.css', [ 'rank-math-analytics-stats' ], rank_math_pro()->version );
		wp_enqueue_script( 'rank-math-analytics-pro-stats', $uri . '/assets/js/admin-bar.js', [ 'rank-math-analytics-stats' ], rank_math_pro()->version, true );
		wp_set_script_translations( 'rank-math-analytics-pro-stats', 'rank-math-pro', RANK_MATH_PRO_PATH . 'languages/' );

		Helper::add_json( 'dateFormat', get_option( 'date_format' ) );
		Helper::add_json( 'isLocalhost', Helper::is_localhost() );
	}

	/**
	 * Add localized data to use in the Post Editor.
	 *
	 * @param array $values Array of localized data.
	 *
	 * @return array
	 */
	public function add_metadata( $values ) {
		$values['isAnalyticsConnected'] = GoogleAnalytics::is_analytics_connected();

		return $values;
	}

	/**
	 * Change user perference.
	 *
	 * @param  array $preference Array of preference.
	 * @return array
	 */
	public function change_user_preference( $preference ) {
		Helper::add_json( 'isAdsenseConnected', ! empty( Adsense::get_adsense_id() ) );
		Helper::add_json( 'isLinkModuleActive', Helper::is_module_active( 'link-counter' ) );
		Helper::add_json( 'isSchemaModuleActive', Helper::is_module_active( 'rich-snippet' ) );
		Helper::add_json( 'isAnalyticsConnected', GoogleAnalytics::is_analytics_connected() );
		Helper::add_json( 'dateFormat', get_option( 'date_format' ) );
		Helper::add_json( 'isLocalhost', Helper::is_localhost() );

		$preference['topKeywords']['ctr']    = false;
		$preference['topKeywords']['ctr']    = false;
		$preference['performance']['clicks'] = false;

		return $preference;
	}

	/**
	 * Data rentention days.
	 *
	 * @return int
	 */
	public function data_retention_period() {
		return 'pro' === Admin_Helper::get_user_plan() ? 180 : 1000;
	}

	/**
	 * Data retrival job gap in seconds.
	 *
	 * @return int
	 */
	public function schedule_gap() {
		return 10;
	}

	/**
	 * Fetch adsense account.
	 *
	 * @param  array $result Result array.
	 * @return array
	 */
	public function check_all_services( $result ) {
		$result['adsenseAccounts'] = Adsense::get_adsense_accounts();

		return $result;
	}

	/**
	 * Add admin bar item.
	 *
	 * @param Admin_Bar_Menu $menu Menu class instance.
	 */
	public function admin_bar_items( $menu ) {
		$post_types = Helper::get_accessible_post_types();
		unset( $post_types['attachment'] );

		if ( is_singular( $post_types ) && Helper::is_post_indexable( get_the_ID() ) ) {
			$menu->add_sub_menu(
				'post_analytics',
				[
					'title'    => esc_html__( 'Post Analytics', 'rank-math-pro' ),
					'href'     => Helper::get_admin_url( 'analytics#/single/' . get_the_ID() ),
					'meta'     => [ 'title' => esc_html__( 'Analytics Report', 'rank-math-pro' ) ],
					'priority' => 20,
				]
			);
		}
	}

	/**
	 * Enqueue scripts for the metabox.
	 */
	public function enqueue_analytics() {
		$screen = get_current_screen();
		if ( 'rank-math_page_rank-math-analytics' !== $screen->id ) {
			return;
		}

		$url = RANK_MATH_PRO_URL . 'includes/modules/analytics/assets/';
		wp_enqueue_style(
			'rank-math-pro-analytics',
			$url . 'css/stats.css',
			null,
			rank_math_pro()->version
		);

		wp_enqueue_script(
			'rank-math-pro-analytics',
			$url . 'js/stats.js',
			[
				'wp-components',
				'wp-element',
				'wp-i18n',
				'wp-date',
				'wp-html-entities',
				'wp-api-fetch',
				'rank-math-analytics',
			],
			rank_math_pro()->version,
			true
		);
		wp_set_script_translations( 'rank-math-pro-analytics', 'rank-math-pro', RANK_MATH_PRO_PATH . 'languages/' );
	}

	/**
	 * Add country dropdown.
	 */
	public function add_country_dropdown3() {
		$profile = wp_parse_args(
			get_option( 'rank_math_google_analytic_profile' ),
			[
				'profile' => '',
				'country' => 'all',
			]
		);
		?>
		<div class="cmb-row-col">
			<label for="site-console-country"><?php esc_html_e( 'Country', 'rank-math-pro' ); ?></label>
			<select class="cmb2_select site-console-country notrack" name="site-console-country" id="site-console-country" disabled="disabled">
				<?php foreach ( ProAdminHelper::choices_countries_3() as $code => $label ) : ?>
					<option value="<?php echo esc_attr( $code ); ?>"<?php selected( $profile['country'], $code ); ?>>
						<?php echo esc_html( $label ); ?>
					</option>
				<?php endforeach; ?>
			</select>
		</div>
		<?php
	}

	/**
	 * Add country dropdown.
	 */
	public function add_country_dropdown2() {
		$analytics = $this->get_settings();
		?>
		<div class="cmb-row-col country-option">
			<label for="site-analytics-country"><?php esc_html_e( 'Country', 'rank-math-pro' ); ?></label>
			<select class="cmb2_select site-analytics-country notrack" name="site-analytics-country" id="site-analytics-country" disabled="disabled">
				<?php foreach ( ProAdminHelper::choices_countries() as $code => $label ) : ?>
					<option value="<?php echo esc_attr( $code ); ?>"<?php selected( $analytics['country'], $code ); ?>>
						<?php echo esc_html( $label ); ?>
					</option>
				<?php endforeach; ?>
			</select>
		</div>
		<?php
	}

	/**
	 * Get Analytics settings.
	 *
	 * @return array
	 */
	public function get_settings() {
		return wp_parse_args(
			get_option( 'rank_math_google_analytic_options' ),
			[
				'adsense_id'       => '',
				'account_id'       => '',
				'property_id'      => '',
				'view_id'          => '',
				'country'          => 'all',
				'install_code'     => false,
				'anonymize_ip'     => false,
				'local_ga_js'      => false,
				'exclude_loggedin' => false,
			]
		);
	}

	/**
	 * Send analytics summary to RankMath.com.
	 */
	public function send_summary() {
		if ( ! Helper::get_settings( 'general.sync_global_setting' ) ) {
			return;
		}

		$registered = Admin_Helper::get_registration_data();
		if ( $registered && isset( $registered['username'] ) && isset( $registered['api_key'] ) ) {
			Stats::get()->set_date_range( '-30 days' );
			$stats = Stats::get()->get_analytics_summary();
			\RankMathPro\Admin\Api::get()->send_summary(
				[
					'username'    => $registered['username'],
					'api_key'     => $registered['api_key'],
					'site_url'    => esc_url( home_url() ),
					'impressions' => array_values( $stats['impressions'] ),
					'clicks'      => array_values( $stats['clicks'] ),
					'keywords'    => array_values( $stats['keywords'] ),
					'pageviews'   => isset( $stats['pageviews'] ) && is_array( $stats['pageviews'] ) ? array_values( $stats['pageviews'] ) : [],
					'adsense'     => isset( $stats['adsense'] ) && is_array( $stats['adsense'] ) ? array_values( $stats['adsense'] ) : [],
				]
			);
		}
	}

	/**
	 * Change option description.
	 */
	public function change_description() {
		return __( 'Enter the number of days to keep Analytics data in your database. The maximum allowed days are 180. Though, 2x data will be stored in the DB for calculating the difference properly.', 'rank-math-pro' );
	}

	/**
	 * Add new settings.
	 *
	 * @param object $cmb CMB2 instance.
	 */
	public function add_new_settings( $cmb ) {
		if ( ! Authentication::is_authorized() ) {
			return;
		}

		$type            = ! ProAdminHelper::is_business_plan() ? 'hidden' : 'toggle';
		$field_ids       = wp_list_pluck( $cmb->prop( 'fields' ), 'id' );
		$fields_position = array_search( 'console_caching_control', array_keys( $field_ids ), true ) + 1;

		$cmb->add_field(
			[
				'id'      => 'sync_global_setting',
				'type'    => $type,
				'name'    => esc_html__( 'Monitor SEO Performance', 'rank-math-pro' ),
				'desc'    => sprintf(
					/* translators: Link to kb article */
					wp_kses_post( __( 'This option allows you to monitor the SEO performance of all of your sites in one centralized dashboard on RankMath.com, so you can check up on sites at a glance. <a href="%1$s" target="_blank">Learn more</a>.', 'rank-math-pro' ) ),
					KB::get( 'monitor-seo-performance', 'Options Panel Analytics Tab Monitor Performance' )
				),
				'default' => 'off',
			],
			++$fields_position
		);

		$cmb->add_field(
			[
				'id'      => 'google_updates',
				'type'    => $type,
				'name'    => esc_html__( 'Google Core Updates in the Graphs', 'rank-math-pro' ),
				'desc'    => sprintf(
					/* translators: Link to kb article */
					__( 'This option allows you to show %s in the Analytics graphs.', 'rank-math-pro' ),
					'<a href="' . KB::get( 'google-updates', 'Google Core Updates in the Graphs' ) . '" target="_blank">' . __( 'Google Core Updates', 'rank-math-pro' ) . '</a>'
				),
				'default' => 'on',
			],
			++$fields_position
		);
	}

	/**
	 * Add/remove/change scheduled action when the report on/off or the frequency options are changed.
	 *
	 * @param string $type     Settings type.
	 * @param array  $settings Settings data.
	 */
	public function before_settings_save( $type, $settings ) {
		if ( $type !== 'general' ) {
			return;
		}

		$value = Helper::get_settings( 'general.sync_global_setting' );
		if ( ! isset( $settings['sync_global_setting'] ) || $settings['sync_global_setting'] === $value ) {
			return;
		}

		\RankMathPro\Admin\Api::get()->sync_setting( $settings['sync_global_setting'] );
		$this->send_summary();
	}

	/**
	 * Check if certain fields got updated.
	 *
	 * @param int   $object_id The ID of the current object.
	 * @param array $updated   Array of field ids that were updated.
	 *                         Will only include field ids that had values change.
	 */
	public function sync_global_settings( $object_id, $updated ) {
		if ( in_array( 'sync_global_setting', $updated, true ) ) {
			\RankMathPro\Admin\Api::get()->sync_setting(
				cmb2_get_option( $object_id, 'sync_global_setting' )
			);

			$this->send_summary();
		}
	}

	/**
	 * Get local Analytics JS URL if the option is turned on.
	 *
	 * @return mixed
	 */
	public function get_local_gtag_js_url() {
		$settings      = $this->get_settings();
		$validator_key = 'rank_math_local_ga_js_validator_' . md5( $settings['property_id'] );
		$validator     = get_transient( $validator_key );
		if ( ! is_string( $validator ) || empty( $validator ) ) {
			$validator = '1';
		}
		return add_query_arg( 'local_ga_js', $validator, trailingslashit( home_url() ) );
	}

	/**
	 * Serve Analytics JS from local cache if the option is turned on.
	 *
	 * @return void
	 */
	public function local_js_endpoint() {
		if ( Param::get( 'local_ga_js' ) && $this->get_settings()['local_ga_js'] && $this->get_local_ga_js_contents() ) {
			header( 'Content-Type: application/javascript' );
			header( 'Cache-Control: max-age=604800, public' );
			echo $this->get_local_ga_js_contents(); // phpcs:ignore
			exit;
		}
	}

	/**
	 * Get local cache of GA JS file contents or fetch new data.
	 *
	 * @param boolean $force_update Force update transient now.
	 * @return string
	 */
	public function get_local_ga_js_contents( $force_update = false ) {
		$settings      = $this->get_settings();
		$cache_key     = 'rank_math_local_ga_js_' . md5( $settings['property_id'] );
		$validator_key = 'rank_math_local_ga_js_validator_' . md5( $settings['property_id'] );
		$validator     = md5( $cache_key . time() );
		$stored        = get_transient( $cache_key );
		if ( false !== $stored && ! $force_update ) {
			return $stored;
		}

		$response = wp_remote_get( 'https://www.googletagmanager.com/gtag/js?id=' . $settings['property_id'] );

		if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
			set_transient( $cache_key, '', 12 * HOUR_IN_SECONDS );
			return '';
		}

		$contents = wp_remote_retrieve_body( $response );
		set_transient( $cache_key, $contents, 12 * HOUR_IN_SECONDS );
		set_transient( $validator_key, $validator, 12 * HOUR_IN_SECONDS );

		return $contents;
	}

	/**
	 * Filter gtag.js config array.
	 *
	 * @param array $config Config parameters.
	 * @return array
	 */
	public function gtag_config( $config ) {
		$settings = $this->get_settings();

		if ( ! empty( $settings['anonymize_ip'] ) ) {
			$config[] = "'anonymize_ip': true";
		}

		return $config;
	}

	/**
	 * Filter function to add Google permissions used in Pro.
	 *
	 * @param array $data Array of System status data.
	 */
	public function google_permission_info( $data ) {
		$permissions  = $data['fields']['permissions']['value'] . ', ';
		$permissions .= esc_html__( 'AdSense: ', 'rank-math-pro' ) . Permissions::get_status_text( Permissions::has_adsense() ) . ', ';
		$permissions .= esc_html__( 'Analytics: ', 'rank-math-pro' ) . Permissions::get_status_text( Permissions::has_analytics() );

		$data['fields']['permissions']['value'] = $permissions;

		return $data;
	}

	/**
	 * Filter inline JS & URL for gtag.js.
	 *
	 * @param array $gtag_data Array containing URL & inline code for the gtag script.
	 * @return array
	 */
	public function gtag( $gtag_data ) {
		if ( is_admin() ) {
			return $gtag_data;
		}

		$settings = $this->get_settings();
		if ( empty( $settings['install_code'] ) ) {
			return $gtag_data;
		}

		if ( ! empty( $settings['local_ga_js'] ) ) {
			$gtag_data['url'] = $this->get_local_gtag_js_url();
		}

		return $gtag_data;
	}

	/**
	 * Filter winning and losing posts if needed.
	 *
	 * @param null  $defaults Null.
	 * @param array $data Analytics data array.
	 * @param array $args Query arguments.
	 *
	 * @return mixed
	 */
	public function filter_winning_losing_posts( $defaults, $data, $args ) {
		$order_by_field = $args['orderBy'];
		$type           = $args['type'];
		$objects        = $args['objects'];

		if ( ! in_array( $type, [ 'win', 'lose' ], true ) ) {
			return $defaults;
		}

		// Filter array by $type value.
		$order_by_position = in_array( $order_by_field, [ 'diffPosition', 'position' ], true ) ? true : false;
		if ( ( 'win' === $type && $order_by_position ) || ( 'lose' === $type && ! $order_by_position ) ) {
			$data = array_filter(
				$data,
				function ( $row ) use ( $order_by_field, $objects ) {
					if ( $objects ) {
						// Show Winning posts if difference is 80 or less.
						return $row[ $order_by_field ] < 0 && $row[ $order_by_field ] > -80;
					}

					return $row[ $order_by_field ] < 0;
				}
			);
		} elseif ( ( 'lose' === $type && $order_by_position ) || ( 'win' === $type && ! $order_by_position ) ) {
			$data = array_filter(
				$data,
				function ( $row ) use ( $order_by_field ) {
					return $row[ $order_by_field ] > 0;
				}
			);
		}

		$data = $this->finalize_filtered_data( $data, $args );

		return $data;
	}

	/**
	 * Filter winning keywords if needed.
	 *
	 * @param null  $defaults Null.
	 * @param array $data Analytics data array.
	 * @param array $args Query arguments.
	 *
	 * @return mixed
	 */
	public function filter_winning_keywords( $defaults, $data, $args ) {
		$order_by_field = $args['orderBy'];
		$dimension      = $args['dimension'];

		if ( 'query' !== $dimension || 'diffPosition' !== $order_by_field || 'ASC' !== $args['order'] ) {
			return $defaults;
		}

		// Filter array by $type value.
		$data = array_filter(
			$data,
			function ( $row ) use ( $order_by_field ) {
				return $row[ $order_by_field ] < 0 && $row[ $order_by_field ] > -80;
			}
		);

		$data = $this->finalize_filtered_data( $data, $args );

		return $data;
	}

	/**
	 * Sort & limit keywords according to the args.
	 *
	 * @param array $data Data rows.
	 * @param array $args Query args.
	 *
	 * @return array
	 */
	private function finalize_filtered_data( $data, $args ) {
		if ( ! empty( $args['order'] ) ) {
			$sort_base_arr = array_column( $data, $args['orderBy'], $args['dimension'] );
			array_multisort( $sort_base_arr, 'ASC' === $args['order'] ? SORT_ASC : SORT_DESC, $data );
		}

		$data = array_slice( $data, $args['offset'], $args['perpage'], true );

		return $data;
	}

	/**
	 * Add search traffic value in seo detail of post lists
	 *
	 * @param int $post_id object Id.
	 */
	public function post_column_search_traffic( $post_id ) {
		if ( ! Authentication::is_authorized() ) {
			return;
		}

		$analytics           = get_option( 'rank_math_google_analytic_options' );
		$analytics_connected = ! empty( $analytics ) && ! empty( $analytics['view_id'] );

		static $traffic_data;
		if ( null === $traffic_data ) {
			$post_ids = $this->get_queried_post_ids();
			if ( empty( $post_ids ) ) {
				$traffic_data = [];
				return;
			}

			$traffic_data = $analytics_connected ? Pageviews::get_traffic_by_object_ids( $post_ids ) : Pageviews::get_impressions_by_object_ids( $post_ids );
		}

		if ( ! isset( $traffic_data[ $post_id ] ) ) {
			return;
		}
		?>
		<span class="rank-math-column-display rank-math-search-traffic">
			<strong>
				<?php
					$analytics_connected
						? esc_html_e( 'Search Traffic:', 'rank-math-pro' )
						: esc_html_e( 'Search Impression:', 'rank-math-pro' );
				?>
			</strong>
			<?php echo esc_html( number_format( $traffic_data[ $post_id ] ) ); ?>
		</span>
		<?php
	}

	/**
	 * Extend the date_exists() function to include the additional tables.
	 *
	 * @param  string $tables Tables.
	 * @return string
	 */
	public function date_exists_tables( $tables ) {
		$tables['analytics'] = DB_Helper::check_table_exists( 'rank_math_analytics_ga' ) ? 'rank_math_analytics_ga' : '';
		$tables['adsense']   = DB_Helper::check_table_exists( 'rank_math_analytics_adsense' ) ? 'rank_math_analytics_adsense' : '';

		return $tables;
	}

	/**
	 * Get queried post ids.
	 */
	private function get_queried_post_ids() {
		global $wp_query;
		if ( empty( $wp_query->posts ) ) {
			return false;
		}

		$post_ids = array_filter(
			array_map(
				function ( $post ) {
					return isset( $post->ID ) ? $post->ID : '';
				},
				$wp_query->posts
			)
		);

		return $post_ids;
	}

	/**
	 * Generate hash for cache key
	 *
	 * @param string $value The cache key value.
	 */
	public function generate_hash( $value = '' ) {
		$new_value = json_decode( $value, true );

		if ( ! $new_value ) {
			return $value;
		}

		$new_value['ai_only'] = ProAdminHelper::ai_traffic_enabled();

		return wp_json_encode( $new_value, true );
	}
}
