<?php
/**
 * AR Analytics bootstrap (shared across AR plugins).
 *
 * @package ARForWordPress
 */

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

if ( ! class_exists( 'AR_Plugin_Analytics' ) ) {

	/**
	 * Central analytics service for AR plugins.
	 */
	class AR_Plugin_Analytics {
		const CPT = 'ar_analytics_event';

		/**
		 * @var bool
		 */
		protected static $bootstrapped = false;

		/**
		 * @var array<string, array>
		 */
		protected static $registered_plugins = array();

		/**
		 * @var string|null
		 */
		protected static $menu_plugin = null;

		/**
		 * Registered admin page hook suffixes for analytics.
		 *
		 * @var array<int,string>
		 */
		protected static $menu_hooks = array();

		/**
		 * Map of parent/menu slug combinations already registered.
		 *
		 * @var array<string,bool>
		 */
		protected static $menu_aliases = array();

		/**
		 * @var array|null
		 */
		protected static $last_summary = null;

		 /**
		  * @var string
		  */
		protected static $rest_namespace = 'ar/v1';

		/**
		 * @var string
		 */
		protected static $rest_route = 'events';

		/**
		 * Deterministic signatures by plugin id.
		 *
		 * @var array<string,string>
		 */
		protected static $plugin_signatures = array();

		/**
		 * Whether the dashboard widget has been registered.
		 *
		 * @var bool
		 */
		protected static $dashboard_widget_registered = false;

		/**
		 * Register plugin configuration.
		 *
		 * @param array $config Config options.
		 */
		public static function boot( array $config ) {
			$config = wp_parse_args(
				$config,
				array(
					'plugin_id'       => '',
					'plugin_name'     => '',
					'text_domain'     => 'ar-for-wordpress',
					'plugin_root_url' => '',
					'version'         => defined( 'AR_VERSION' ) ? AR_VERSION : '1.0.0',
					'register_menu'   => true,
					'menu_slug'       => 'ar-analytics',
					'menu_title'      => __( 'AR Insights', 'ar-for-wordpress' ),
					'page_title'      => __( 'AR Insights Dashboard', 'ar-for-wordpress' ),
					'view_capability' => 'manage_options',
					'rest_namespace'  => 'ar/v1',
					'rest_route'      => 'events',
					'signature'       => null,
					'parent_slug'     => '',
				)
			);

			if ( empty( $config['plugin_id'] ) ) {
				return;
			}

			$config['plugin_id'] = self::normalize_plugin_id( $config['plugin_id'] );

			if ( empty( $config['signature'] ) ) {
				$config['signature'] = self::generate_signature( $config['plugin_id'] );
			}

			self::$registered_plugins[ $config['plugin_id'] ] = $config;
			self::$plugin_signatures[ $config['plugin_id'] ] = $config['signature'];

			if ( ! self::$bootstrapped ) {
				self::$rest_namespace = $config['rest_namespace'];
				self::$rest_route     = $config['rest_route'];
				self::register_hooks();
				self::$bootstrapped = true;
			}

			if ( ! self::$menu_plugin && ! empty( $config['register_menu'] ) ) {
				self::$menu_plugin = $config['plugin_id'];
			}
		}

		/**
		 * Register global hooks once.
		 */
		protected static function register_hooks() {
			add_action( 'init', array( __CLASS__, 'register_post_type' ) );
			add_action( 'init', array( __CLASS__, 'maybe_schedule_cleanup' ), 20 );
			add_action( 'rest_api_init', array( __CLASS__, 'register_rest_routes' ) );
			add_action( 'admin_menu', array( __CLASS__, 'register_menu' ) );
			add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_admin_assets' ) );
			add_action( 'wp_dashboard_setup', array( __CLASS__, 'register_dashboard_widget' ) );
			add_action( 'ar_analytics_purge_events', array( __CLASS__, 'purge_old_events' ) );
		}

		/**
		 * Register the analytics CPT.
		 */
		public static function register_post_type() {
			$args = array(
				'labels'              => array(
					'name'          => __( 'AR Analytics Events', 'ar-for-wordpress' ),
					'singular_name' => __( 'AR Analytics Event', 'ar-for-wordpress' ),
				),
				'public'              => false,
				'show_ui'             => false,
				'show_in_menu'        => false,
				'show_in_nav_menus'   => false,
				'show_in_admin_bar'   => false,
				'supports'            => array( 'title' ),
				'capability_type'     => 'post',
				'map_meta_cap'        => true,
				'has_archive'         => false,
				'delete_with_user'    => false,
				'publicly_queryable'  => false,
				'exclude_from_search' => true,
			);

			register_post_type( self::CPT, $args );
		}

		/**
		 * Register REST routes.
		 */
		public static function register_rest_routes() {
			register_rest_route(
				self::$rest_namespace,
				'/' . self::$rest_route,
				array(
					array(
						'methods'             => WP_REST_Server::CREATABLE,
						'callback'            => array( __CLASS__, 'handle_event_ingest' ),
						'permission_callback' => '__return_true',
						'args'                => array(
							'events' => array(
								'type'     => 'array',
								'required' => true,
							),
						),
					),
				)
			);
		}

		/**
		 * Validate incoming request nonce.
		 *
		 * @param WP_REST_Request $request Request object.
		 * @return bool
		 */
		public static function validate_request_nonce( WP_REST_Request $request ) {
			$nonce = $request->get_header( 'x_wp_nonce' );

			if ( empty( $nonce ) ) {
				$nonce = $request->get_param( '_wpnonce' );
			}

			if ( empty( $nonce ) && isset( $request['nonce'] ) ) {
				$nonce = $request['nonce'];
			}

		$json_params = $request->get_json_params();

		if ( empty( $nonce ) && isset( $json_params['nonce'] ) ) {
			$nonce = $json_params['nonce'];
		}

		if ( $nonce && ( wp_verify_nonce( $nonce, 'ar_analytics_event' ) || wp_verify_nonce( $nonce, 'wp_rest' ) ) ) {
			return true;
		}

		$signature = self::extract_signature_from_request( $request, $json_params );
		$plugin_id = self::extract_plugin_id_from_request( $json_params );

		if ( $signature && $plugin_id && self::verify_signature( $plugin_id, $signature ) ) {
			return true;
		}

		if ( self::is_same_origin_request( $request ) ) {
			return true;
		}

		if ( $plugin_id && isset( self::$registered_plugins[ $plugin_id ] ) ) {
			if ( apply_filters( 'ar_analytics_allow_unsigned', true, $plugin_id, $request ) ) {
				return true;
			}
		}

			// Allow manual testing from privileged users.
			if ( current_user_can( 'manage_options' ) ) {
				return true;
			}

		self::log_permission_failure(
			array(
				'nonce'     => $nonce,
				'plugin_id' => $plugin_id,
				'signature' => $signature,
				'origin'    => $request->get_header( 'origin' ),
				'referer'   => $request->get_header( 'referer' ),
			)
		);

			return false;
		}

		/**
		 * Handle event ingestion.
		 *
		 * @param WP_REST_Request $request Request.
		 * @return WP_REST_Response|WP_Error
		 */
		public static function handle_event_ingest( WP_REST_Request $request ) {
			$params = $request->get_json_params();
			$events = isset( $params['events'] ) && is_array( $params['events'] ) ? $params['events'] : array();

			if ( empty( $events ) ) {
				return new WP_Error(
					'ar_analytics_empty',
					__( 'No analytics events supplied.', 'ar-for-wordpress' ),
					array( 'status' => 400 )
				);
			}

			$allowed_types = apply_filters(
				'ar_analytics_event_types',
				array( 'viewer_load', 'ar_launch', 'variant_change' )
			);

			$stored   = 0;
			$rejected = 0;

			foreach ( $events as $event ) {
				$normalized = self::sanitize_event( $event, $allowed_types );

				if ( empty( $normalized ) ) {
					$rejected++;
					continue;
				}

				if ( ! self::is_enabled_for_plugin( $normalized['plugin_id'] ) ) {
					$rejected++;
					continue;
				}

				$allow_event = apply_filters( 'ar_analytics_allow_event', true, $normalized );
				if ( ! $allow_event ) {
					$rejected++;
					continue;
				}

				$event_id = self::store_event( $normalized );

				if ( $event_id ) {
					$stored++;
				} else {
					$rejected++;
				}
			}

			return rest_ensure_response(
				array(
					'stored'   => $stored,
					'rejected' => $rejected,
				)
			);
		}

		/**
		 * Sanitize an incoming event array.
		 *
		 * @param mixed $event Raw event.
		 * @param array $allowed_types Allowed event keys.
		 * @return array|null
		 */
		protected static function sanitize_event( $event, array $allowed_types ) {
			if ( ! is_array( $event ) ) {
				return null;
			}

			$plugin_id = isset( $event['pluginId'] ) ? sanitize_key( $event['pluginId'] ) : '';

			if ( empty( $plugin_id ) || ! isset( self::$registered_plugins[ $plugin_id ] ) ) {
				return null;
			}

			$type = isset( $event['type'] ) ? sanitize_key( $event['type'] ) : '';

			if ( empty( $type ) || ! in_array( $type, $allowed_types, true ) ) {
				return null;
			}

			$model_id    = isset( $event['modelId'] ) ? sanitize_text_field( $event['modelId'] ) : '';
			$instance_id = isset( $event['instanceId'] ) ? sanitize_text_field( $event['instanceId'] ) : '';
			$variant     = isset( $event['variant'] ) ? sanitize_text_field( $event['variant'] ) : '';
			$page_url    = isset( $event['pageUrl'] ) ? esc_url_raw( $event['pageUrl'] ) : '';
			$referrer    = isset( $event['referrer'] ) ? esc_url_raw( $event['referrer'] ) : '';
			$device      = isset( $event['device'] ) ? sanitize_text_field( $event['device'] ) : '';
			$client_id   = isset( $event['clientId'] ) ? sanitize_text_field( $event['clientId'] ) : '';
			$timestamp   = isset( $event['timestamp'] ) ? (int) $event['timestamp'] : 0;

			if ( $timestamp > 9999999999 ) {
				$timestamp = (int) round( $timestamp / 1000 );
			}

			$max_allowed = time() + YEAR_IN_SECONDS;
			if ( $timestamp > $max_allowed ) {
				$timestamp = time();
			}

			if ( $timestamp <= 0 ) {
				$timestamp = time();
			}

			$context = array();
			if ( ! empty( $event['context'] ) && is_array( $event['context'] ) ) {
				$context = self::sanitize_context( $event['context'] );
			}

			$user_agent = '';
			if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
				$user_agent = substr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), 0, 255 );
			}

			return array(
				'plugin_id'  => $plugin_id,
				'type'       => $type,
				'model_id'   => substr( $model_id, 0, 64 ),
				'instance_id'=> substr( $instance_id, 0, 120 ),
				'variant'    => substr( $variant, 0, 160 ),
				'page_url'   => $page_url,
				'referrer'   => $referrer,
				'device'     => substr( $device, 0, 32 ),
				'client_id'  => substr( $client_id, 0, 120 ),
				'context'    => $context,
				'timestamp'  => $timestamp,
				'user_agent' => $user_agent,
			);
		}

		/**
		 * Sanitize context payload.
		 *
		 * @param array $context Raw context.
		 * @return array
		 */
		protected static function sanitize_context( array $context ) {
			$allowed_keys = array(
				'postId',
				'postType',
				'productId',
				'pageTemplate',
				'siteLanguage',
			);

			$clean = array();

			foreach ( $context as $key => $value ) {
				if ( in_array( $key, $allowed_keys, true ) ) {
					if ( is_scalar( $value ) ) {
						$clean[ $key ] = sanitize_text_field( (string) $value );
					}
				}
			}

			return $clean;
		}

		/**
		 * Persist an event.
		 *
		 * @param array $event Normalized event.
		 * @return int Event post ID.
		 */
		protected static function store_event( array $event ) {
			$event_date_gmt = gmdate( 'Y-m-d H:i:s', $event['timestamp'] );
			$event_date     = get_date_from_gmt( $event_date_gmt );

			$postarr = array(
				'post_type'      => self::CPT,
				'post_status'    => 'publish',
				'post_title'     => strtoupper( $event['type'] ) . ' - ' . ( $event['model_id'] ? $event['model_id'] : __( 'Unknown Model', 'ar-for-wordpress' ) ),
				'post_author'    => 0,
				'post_date_gmt'  => $event_date_gmt,
				'post_date'      => $event_date,
				'post_content'   => '',
				'comment_status' => 'closed',
				'ping_status'    => 'closed',
			);

			$post_id = wp_insert_post( $postarr, true, false );

			if ( is_wp_error( $post_id ) ) {
				return 0;
			}

			add_post_meta( $post_id, '_ar_event_type', $event['type'], true );
			add_post_meta( $post_id, '_ar_plugin_id', $event['plugin_id'], true );

			if ( ! empty( $event['model_id'] ) ) {
				add_post_meta( $post_id, '_ar_model_id', $event['model_id'], true );
			}

			if ( ! empty( $event['instance_id'] ) ) {
				add_post_meta( $post_id, '_ar_instance_id', $event['instance_id'], true );
			}

			if ( ! empty( $event['variant'] ) ) {
				add_post_meta( $post_id, '_ar_variant', $event['variant'], true );
			}

			if ( ! empty( $event['page_url'] ) ) {
				add_post_meta( $post_id, '_ar_page_url', $event['page_url'], true );
			}

			if ( ! empty( $event['referrer'] ) ) {
				add_post_meta( $post_id, '_ar_referrer', $event['referrer'], true );
			}

			if ( ! empty( $event['device'] ) ) {
				add_post_meta( $post_id, '_ar_device', $event['device'], true );
			}

			if ( ! empty( $event['client_id'] ) ) {
				add_post_meta( $post_id, '_ar_client_id', $event['client_id'], true );
			}

			if ( ! empty( $event['user_agent'] ) ) {
				add_post_meta( $post_id, '_ar_user_agent', $event['user_agent'], true );
			}

			if ( ! empty( $event['context'] ) ) {
				add_post_meta( $post_id, '_ar_context', wp_json_encode( $event['context'] ) );
			}

			return (int) $post_id;
		}

		/**
		 * Schedule cleanup if needed.
		 */
		public static function maybe_schedule_cleanup() {
			if ( ! wp_next_scheduled( 'ar_analytics_purge_events' ) ) {
				wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', 'ar_analytics_purge_events' );
			}
		}

		/**
		 * Purge events older than retention window.
		 */
		public static function purge_old_events() {
			$retention_days = (int) apply_filters( 'ar_analytics_retention_days', 90 );

			if ( $retention_days <= 0 ) {
				return;
			}

			$cutoff = gmdate( 'Y-m-d H:i:s', time() - ( $retention_days * DAY_IN_SECONDS ) );

			$query = new WP_Query(
				array(
					'post_type'      => self::CPT,
					'post_status'    => 'publish',
					'posts_per_page' => 100,
					'date_query'     => array(
						array(
							'before'    => $cutoff,
							'inclusive' => true,
							'column'    => 'post_date_gmt',
						),
					),
					'fields'         => 'ids',
				)
			);

			if ( empty( $query->posts ) ) {
				return;
			}

			foreach ( $query->posts as $post_id ) {
				wp_delete_post( $post_id, true );
			}
		}

		/**
		 * Determine if analytics is enabled for plugin.
		 *
		 * @param string $plugin_id Plugin ID.
		 * @return bool
		 */
		public static function is_enabled_for_plugin( $plugin_id ) {
			$enabled = apply_filters( 'ar_analytics_enabled', true, $plugin_id );
			return (bool) ( $enabled && isset( self::$registered_plugins[ $plugin_id ] ) );
		}

		/**
		 * Enqueue admin assets on the insights page.
		 *
		 * @param string $hook Current admin hook.
		 */
		public static function enqueue_admin_assets( $hook ) {
			$config = self::get_menu_plugin_config();

			if ( empty( $config ) ) {
				return;
			}

			$hooks = self::$menu_hooks;
			if ( empty( $hooks ) ) {
				$hooks = array( 'toplevel_page_' . $config['menu_slug'] );
			}

			if ( ! in_array( $hook, $hooks, true ) ) {
				return;
			}

			$range_days   = self::get_requested_range_days();
			$plugin_scope = self::get_requested_plugin_scope();

			$summary                = self::get_summary(
				array(
					'plugin_id' => $plugin_scope,
					'days'      => $range_days,
				)
			);
			self::$last_summary = $summary;

			if ( ! empty( $config['plugin_root_url'] ) ) {
				wp_enqueue_style(
					'ar-analytics-dashboard',
					trailingslashit( $config['plugin_root_url'] ) . 'assets/admin/ar-analytics-dashboard.css',
					array(),
					$config['version']
				);

				wp_enqueue_script(
					'ar-analytics-dashboard',
					trailingslashit( $config['plugin_root_url'] ) . 'assets/admin/ar-analytics-dashboard.js',
					array(),
					$config['version'],
					true
				);
			}

			wp_localize_script(
				'ar-analytics-dashboard',
				'ARAnalyticsDashboardData',
				array(
					'rangeDays'       => $range_days,
					'pluginFilter'    => $plugin_scope,
					'dailySeries'     => isset( $summary['daily_series'] ) ? $summary['daily_series'] : array(),
					'deviceBreakdown' => isset( $summary['device_breakdown'] ) ? $summary['device_breakdown'] : array(),
					'labels'          => array(
						'dailyEvents'   => __( 'Events', $config['text_domain'] ),
						'devices'       => __( 'Devices', $config['text_domain'] ),
						'day'           => __( 'Day', $config['text_domain'] ),
						'count'         => __( 'Count', $config['text_domain'] ),
						'noData'        => __( 'Insufficient data to render chart.', $config['text_domain'] ),
					),
				)
			);
		}

		/**
		 * Register admin menu page.
		 */
		public static function register_menu() {
			$config = self::get_menu_plugin_config();

			if ( empty( $config ) ) {
				return;
			}

			$parent_slug = isset( $config['parent_slug'] ) ? $config['parent_slug'] : '';
			$alias_key   = $parent_slug ? $parent_slug . '|' . $config['menu_slug'] : null;

			$hook_suffix = null;

			if ( $parent_slug ) {
				$hook_suffix = add_submenu_page(
					$parent_slug,
					$config['page_title'],
					$config['menu_title'],
					$config['view_capability'],
					$config['menu_slug'],
					array( __CLASS__, 'render_admin_page' )
				);
			} else {
				$hook_suffix = add_menu_page(
					$config['page_title'],
					$config['menu_title'],
					$config['view_capability'],
					$config['menu_slug'],
					array( __CLASS__, 'render_admin_page' ),
					'dashicons-chart-area',
					6
				);
			}

			if ( $hook_suffix ) {
				self::$menu_hooks[] = $hook_suffix;
				if ( $alias_key ) {
					self::$menu_aliases[ $alias_key ] = true;
				}
			}

			self::register_additional_parent_aliases();
		}

		/**
		 * Render admin page.
		 */
		public static function render_admin_page() {
			if ( ! current_user_can( 'manage_options' ) ) {
				return;
			}

			$config = self::get_menu_plugin_config();

			if ( empty( $config ) ) {
				return;
			}

			$summary = self::$last_summary;

			if ( null === $summary ) {
				$summary = self::get_summary(
					array(
						'plugin_id' => self::get_requested_plugin_scope(),
						'days'      => self::get_requested_range_days(),
					)
				);
			}

			$plugin_scope = self::get_requested_plugin_scope();
			$range_days   = self::get_requested_range_days();

			$plugins = self::$registered_plugins;

			include dirname( __FILE__ ) . '/views/ar-analytics-dashboard.php';
		}

		/**
		 * Ensure analytics appears under any plugin-defined parent menus.
		 *
		 * @return void
		 */
		protected static function register_additional_parent_aliases() {
			if ( empty( self::$registered_plugins ) ) {
				return;
			}

			foreach ( self::$registered_plugins as $plugin_config ) {
				if ( empty( $plugin_config['parent_slug'] ) ) {
					continue;
				}

				$alias_key = $plugin_config['parent_slug'] . '|' . $plugin_config['menu_slug'];

				if ( isset( self::$menu_aliases[ $alias_key ] ) ) {
					continue;
				}

				$hook_suffix = add_submenu_page(
					$plugin_config['parent_slug'],
					$plugin_config['page_title'],
					$plugin_config['menu_title'],
					$plugin_config['view_capability'],
					$plugin_config['menu_slug'],
					array( __CLASS__, 'render_admin_page' )
				);

				if ( $hook_suffix ) {
					self::$menu_aliases[ $alias_key ] = true;
					self::$menu_hooks[]              = $hook_suffix;
				}
			}
		}

		/**
		 * Public wrapper to ensure additional aliases are registered.
		 *
		 * @return void
		 */
		public static function ensure_parent_aliases() {
			self::register_additional_parent_aliases();
		}

		/**
		 * Allow external code to register additional menu hooks.
		 *
		 * @param string      $hook_suffix Hook suffix returned by add_menu_page/add_submenu_page.
		 * @param string|null $parent_slug Optional parent slug for alias tracking.
		 * @param string|null $menu_slug   Optional menu slug for alias tracking.
		 * @return void
		 */
		public static function register_menu_hook( $hook_suffix, $parent_slug = null, $menu_slug = null ) {
			if ( empty( $hook_suffix ) ) {
				return;
			}

			if ( ! in_array( $hook_suffix, self::$menu_hooks, true ) ) {
				self::$menu_hooks[] = $hook_suffix;
			}

			if ( $parent_slug && $menu_slug ) {
				$alias_key = $parent_slug . '|' . $menu_slug;
				self::$menu_aliases[ $alias_key ] = true;
			}
		}

		/**
		 * Compute analytics summary dataset.
		 *
		 * @param array $args Arguments.
		 * @return array
		 */
		public static function get_summary( array $args = array() ) {
			global $wpdb;

			$args = wp_parse_args(
				$args,
				array(
					'plugin_id' => null,
					'days'      => 7,
					'limit'     => 5,
				)
			);

			$days           = max( 1, (int) $args['days'] );
			$plugin_id      = $args['plugin_id'];
			$limit          = max( 1, (int) $args['limit'] );
			$cutoff_gmt     = gmdate( 'Y-m-d H:i:s', time() - ( $days * DAY_IN_SECONDS ) );
			$posts_table    = $wpdb->posts;
			$postmeta_table = $wpdb->postmeta;

			$where  = $wpdb->prepare( "WHERE p.post_type = %s AND p.post_status = 'publish' AND p.post_date_gmt >= %s", self::CPT, $cutoff_gmt );
			$params = array();

			if ( ! empty( $plugin_id ) ) {
				$where      .= $wpdb->prepare( " AND plugin_meta.meta_value = %s", $plugin_id );
				$params[]    = $plugin_id;
			}

			// Event counts.
			$sql_counts = "
				SELECT type_meta.meta_value AS event_type, COUNT(*) AS total
				FROM {$posts_table} p
				INNER JOIN {$postmeta_table} type_meta ON type_meta.post_id = p.ID AND type_meta.meta_key = '_ar_event_type'
				INNER JOIN {$postmeta_table} plugin_meta ON plugin_meta.post_id = p.ID AND plugin_meta.meta_key = '_ar_plugin_id'
				{$where}
				GROUP BY type_meta.meta_value
			";

			$counts_raw = $wpdb->get_results( $sql_counts, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
			$counts     = array(
				'total'          => 0,
				'viewer_load'    => 0,
				'ar_launch'      => 0,
				'variant_change' => 0,
			);

			foreach ( (array) $counts_raw as $row ) {
				$type = isset( $row['event_type'] ) ? $row['event_type'] : '';
				$cnt  = isset( $row['total'] ) ? (int) $row['total'] : 0;

				if ( $type ) {
					$counts[ $type ] = $cnt;
					$counts['total'] += $cnt;
				}
			}

			// Daily series.
			$sql_daily = "
				SELECT DATE(p.post_date_gmt) AS event_day, COUNT(*) AS total
				FROM {$posts_table} p
				INNER JOIN {$postmeta_table} plugin_meta ON plugin_meta.post_id = p.ID AND plugin_meta.meta_key = '_ar_plugin_id'
				{$where}
				GROUP BY DATE(p.post_date_gmt)
				ORDER BY event_day ASC
			";

			$daily_raw = $wpdb->get_results( $sql_daily, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.directQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
			$daily     = array();

			$start = new DateTimeImmutable( gmdate( 'Y-m-d', time() - ( ( $days - 1 ) * DAY_IN_SECONDS ) ) );
			for ( $i = 0; $i < $days; $i++ ) {
				$day_key        = $start->modify( "+{$i} days" )->format( 'Y-m-d' );
				$daily[ $day_key ] = 0;
			}

			foreach ( (array) $daily_raw as $row ) {
				$day = isset( $row['event_day'] ) ? $row['event_day'] : '';
				if ( isset( $daily[ $day ] ) ) {
					$daily[ $day ] = (int) $row['total'];
				}
			}

			$daily_series = array();
			foreach ( $daily as $day => $count ) {
				$daily_series[] = array(
					'day'   => $day,
					'count' => $count,
				);
			}

			// Device breakdown.
			$sql_devices = "
				SELECT device_meta.meta_value AS device, COUNT(*) AS total
				FROM {$posts_table} p
				INNER JOIN {$postmeta_table} device_meta ON device_meta.post_id = p.ID AND device_meta.meta_key = '_ar_device'
				INNER JOIN {$postmeta_table} plugin_meta ON plugin_meta.post_id = p.ID AND plugin_meta.meta_key = '_ar_plugin_id'
				{$where}
				GROUP BY device_meta.meta_value
			";

			$device_raw = $wpdb->get_results( $sql_devices, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
			$devices    = array();

			foreach ( (array) $device_raw as $row ) {
				$device = ! empty( $row['device'] ) ? $row['device'] : __( 'Unknown', 'ar-for-wordpress' );
				$devices[] = array(
					'label' => $device,
					'count' => (int) $row['total'],
				);
			}

			// Top models/products.
			$sql_models = "
				SELECT model_meta.meta_value AS model_id, COUNT(*) AS total
				FROM {$posts_table} p
				INNER JOIN {$postmeta_table} model_meta ON model_meta.post_id = p.ID AND model_meta.meta_key = '_ar_model_id'
				INNER JOIN {$postmeta_table} plugin_meta ON plugin_meta.post_id = p.ID AND plugin_meta.meta_key = '_ar_plugin_id'
				{$where}
				GROUP BY model_meta.meta_value
				ORDER BY total DESC
				LIMIT %d
			";

			$sql_models = $wpdb->prepare( $sql_models, $limit ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
			$models_raw = $wpdb->get_results( $sql_models, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

			$top_models = array();
			foreach ( (array) $models_raw as $row ) {
				$model_id = isset( $row['model_id'] ) ? $row['model_id'] : '';
				$count    = isset( $row['total'] ) ? (int) $row['total'] : 0;
				$title    = $model_id && is_numeric( $model_id ) ? get_the_title( (int) $model_id ) : '';

				$top_models[] = array(
					'id'    => $model_id,
					'title' => $title ? $title : $model_id,
					'count' => $count,
				);
			}

			// Top pages.
			$sql_pages = "
				SELECT page_meta.meta_value AS page_url, COUNT(*) AS total
				FROM {$posts_table} p
				INNER JOIN {$postmeta_table} page_meta ON page_meta.post_id = p.ID AND page_meta.meta_key = '_ar_page_url'
				INNER JOIN {$postmeta_table} plugin_meta ON plugin_meta.post_id = p.ID AND plugin_meta.meta_key = '_ar_plugin_id'
				{$where}
				GROUP BY page_meta.meta_value
				ORDER BY total DESC
				LIMIT %d
			";

			$sql_pages = $wpdb->prepare( $sql_pages, $limit ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
			$pages_raw = $wpdb->get_results( $sql_pages, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

			$top_pages = array();
			foreach ( (array) $pages_raw as $row ) {
				$url   = isset( $row['page_url'] ) ? $row['page_url'] : '';
				$count = isset( $row['total'] ) ? (int) $row['total'] : 0;
				$label = $url ? self::humanize_url( $url ) : __( 'Unknown', 'ar-for-wordpress' );
				$top_pages[] = array(
					'url'   => $url,
					'label' => $label,
					'count' => $count,
				);
			}

			$recent_events = self::get_recent_events( $plugin_id );

			return array(
				'counts'          => $counts,
				'daily_series'    => $daily_series,
				'device_breakdown'=> $devices,
				'top_models'      => $top_models,
				'top_pages'       => $top_pages,
				'recent_events'   => $recent_events,
			);
		}

		/**
		 * Fetch recent events.
		 *
		 * @param string|null $plugin_id Plugin scope.
		 * @return array
		 */
		protected static function get_recent_events( $plugin_id ) {
			$args = array(
				'post_type'      => self::CPT,
				'post_status'    => 'publish',
				'posts_per_page' => 10,
				'orderby'        => 'date',
				'order'          => 'DESC',
				'fields'         => 'ids',
				'meta_query'     => array(),
			);

			if ( $plugin_id ) {
				$args['meta_query'][] = array(
					'key'   => '_ar_plugin_id',
					'value' => $plugin_id,
				);
			}

			$query = new WP_Query( $args );

			if ( empty( $query->posts ) ) {
				return array();
			}

			$events = array();
			foreach ( $query->posts as $post_id ) {
				$events[] = array(
					'type'      => get_post_meta( $post_id, '_ar_event_type', true ),
					'model_id'  => get_post_meta( $post_id, '_ar_model_id', true ),
					'variant'   => get_post_meta( $post_id, '_ar_variant', true ),
					'page_url'  => get_post_meta( $post_id, '_ar_page_url', true ),
					'device'    => get_post_meta( $post_id, '_ar_device', true ),
					'timestamp' => get_post_time( 'U', true, $post_id ),
				);
			}

			return $events;
		}

		/**
		 * Convert URL to human friendly label.
		 *
		 * @param string $url Url.
		 * @return string
		 */
		protected static function humanize_url( $url ) {
			if ( empty( $url ) ) {
				return '';
			}

			$parsed = wp_parse_url( $url );

			if ( empty( $parsed ) ) {
				return $url;
			}

			$path = isset( $parsed['path'] ) ? trim( $parsed['path'], '/' ) : '';

			if ( empty( $path ) ) {
				return isset( $parsed['host'] ) ? $parsed['host'] : $url;
			}

			return $path;
		}

		/**
		 * Build analytics context for templates.
		 *
		 * @param array $extra Extra context.
		 * @return array
		 */
		public static function build_context( array $extra = array() ) {
			$post_id   = get_the_ID();
			$post_type = $post_id ? get_post_type( $post_id ) : '';
			$template  = function_exists( 'get_page_template_slug' ) ? get_page_template_slug( $post_id ) : '';

			$context = array(
				'postId'       => $post_id ? (string) $post_id : '',
				'postType'     => $post_type ? (string) $post_type : '',
				'pageTemplate' => $template ? (string) $template : '',
				'siteLanguage' => get_bloginfo( 'language' ),
			);

			if ( function_exists( 'wc_get_product' ) && $post_id && 'product' === $post_type ) {
				$context['productId'] = (string) $post_id;
			}

			$context = array_merge( $context, $extra );

			return array_filter( $context );
		}

		/**
		 * Fetch script settings for a plugin.
		 *
		 * @param string $plugin_id Plugin identifier.
		 * @return array|null
		 */
		public static function get_script_settings_for_plugin( $plugin_id ) {
			$plugin_id = self::normalize_plugin_id( $plugin_id );
			if ( empty( $plugin_id ) || ! isset( self::$registered_plugins[ $plugin_id ] ) ) {
				return null;
			}

			$config  = self::$registered_plugins[ $plugin_id ];
			$enabled = self::is_enabled_for_plugin( $plugin_id );

			$rest_url = rest_url( trailingslashit( self::$rest_namespace ) . self::$rest_route );

			return array(
				'enabled'      => $enabled,
				'pluginId'     => $plugin_id,
				'restUrl'      => esc_url_raw( $rest_url ),
				'nonce'        => wp_create_nonce( 'wp_rest' ),
				'signature'    => self::get_signature_for_plugin( $plugin_id ),
				'batchSize'    => (int) apply_filters( 'ar_analytics_batch_size', 6, $plugin_id ),
				'flushInterval'=> (int) apply_filters( 'ar_analytics_flush_interval', 3000, $plugin_id ),
				'debug'        => (bool) apply_filters( 'ar_analytics_debug', defined( 'WP_DEBUG' ) && WP_DEBUG, $plugin_id ),
				'googleAnalytics' => self::get_google_analytics_settings( $plugin_id ),
			);
		}

		/**
		 * Build Google Analytics integration settings for scripts.
		 *
		 * @param string $plugin_id Plugin identifier.
		 * @return array
		 */
		protected static function get_google_analytics_settings( $plugin_id ) {
			$defaults = array(
				'enabled'             => false,
				'mode'                => 'auto',
				'sendTo'              => '',
				'sendToList'          => array(),
				'transport'           => '',
				'eventNamePrefix'     => 'ar_',
				'eventNameMap'        => array(),
				'parameters'          => array(),
				'gtagParameters'      => array(),
				'dataLayerName'       => 'dataLayer',
				'dataLayerParameters' => array(),
			);

			$settings = apply_filters( 'ar_analytics_google_analytics_settings', $defaults, $plugin_id );
			$settings = apply_filters( 'ar_analytics_google_analytics_settings_' . $plugin_id, $settings, $plugin_id );

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

			$settings = wp_parse_args( $settings, $defaults );

			$settings['enabled'] = (bool) $settings['enabled'];

			$allowed_modes = array( 'auto', 'gtag', 'datalayer', 'both' );
			$mode          = is_string( $settings['mode'] ) ? strtolower( $settings['mode'] ) : '';
			if ( ! in_array( $mode, $allowed_modes, true ) ) {
				$mode = 'auto';
			}
			$settings['mode'] = 'datalayer' === $mode ? 'dataLayer' : $mode;

			$settings['sendTo'] = is_string( $settings['sendTo'] ) ? trim( $settings['sendTo'] ) : '';

			if ( ! is_array( $settings['sendToList'] ) ) {
				$settings['sendToList'] = array();
			} else {
				$settings['sendToList'] = array_values(
					array_filter(
						array_map(
							static function ( $value ) {
								return is_string( $value ) ? trim( $value ) : '';
							},
							$settings['sendToList']
						)
					)
				);
			}

			$settings['transport'] = is_string( $settings['transport'] ) ? strtolower( preg_replace( '/[^a-z0-9_]/i', '', $settings['transport'] ) ) : '';

			$settings['eventNamePrefix'] = is_string( $settings['eventNamePrefix'] ) ? preg_replace( '/[^a-z0-9_]/i', '_', strtolower( $settings['eventNamePrefix'] ) ) : 'ar_';
			if ( '' === $settings['eventNamePrefix'] ) {
				$settings['eventNamePrefix'] = 'ar_';
			}

			if ( ! is_array( $settings['eventNameMap'] ) ) {
				$settings['eventNameMap'] = array();
			} else {
				$map = array();
				foreach ( $settings['eventNameMap'] as $key => $value ) {
					if ( ! is_string( $key ) || '' === $key || ! is_string( $value ) ) {
						continue;
					}

					$key   = sanitize_key( $key );
					$value = preg_replace( '/[^a-z0-9_]/i', '_', $value );
					if ( '' === $value ) {
						continue;
					}

					$map[ $key ] = strtolower( $value );
				}

				$settings['eventNameMap'] = $map;
			}

			$measurement_id = self::sanitize_google_analytics_destination( get_option( 'ar_ga_measurement_id', '' ) );
			if ( $measurement_id ) {
				$settings['enabled'] = true;

				if ( empty( $settings['mode'] ) || 'auto' === $settings['mode'] ) {
					$settings['mode'] = 'gtag';
				}

				if ( empty( $settings['sendTo'] ) ) {
					$settings['sendTo'] = $measurement_id;
				}

				if ( ! in_array( $measurement_id, $settings['sendToList'], true ) ) {
					$settings['sendToList'][] = $measurement_id;
				}

				if ( ! isset( $settings['parameters'] ) || ! is_array( $settings['parameters'] ) ) {
					$settings['parameters'] = array();
				}

				if ( empty( $settings['parameters']['event_category'] ) ) {
					$settings['parameters']['event_category'] = 'ar_insights';
				}

				if ( empty( $settings['parameters']['event_label'] ) ) {
					$settings['parameters']['event_label'] = $plugin_id;
				}
			}

			$param_sets = array( 'parameters', 'gtagParameters', 'dataLayerParameters' );
			foreach ( $param_sets as $param_key ) {
				if ( ! isset( $settings[ $param_key ] ) || ! is_array( $settings[ $param_key ] ) ) {
					$settings[ $param_key ] = array();
					continue;
				}

				$params = array();
				foreach ( $settings[ $param_key ] as $key => $value ) {
					if ( ! is_string( $key ) || '' === $key ) {
						continue;
					}

					$sanitized_key = preg_replace( '/[^a-zA-Z0-9_]/', '_', $key );
					if ( '' === $sanitized_key ) {
						continue;
					}

					if ( is_scalar( $value ) || null === $value ) {
						$params[ $sanitized_key ] = $value;
					} else {
						$params[ $sanitized_key ] = wp_json_encode( $value );
					}
				}

				$settings[ $param_key ] = $params;
			}

			$settings['dataLayerName'] = is_string( $settings['dataLayerName'] ) && $settings['dataLayerName']
				? preg_replace( '/[^a-zA-Z0-9_]/', '', $settings['dataLayerName'] )
				: 'dataLayer';

			return $settings;
		}

		/**
		 * Sanitize Google Analytics destinations such as measurement IDs.
		 *
		 * @param string $value Raw destination value.
		 * @return string
		 */
		protected static function sanitize_google_analytics_destination( $value ) {
			if ( ! is_string( $value ) ) {
				return '';
			}

			$value = trim( $value );
			if ( '' === $value ) {
				return '';
			}

			$value = preg_replace( '/\s+/', '', $value );
			$value = preg_replace( '/[^A-Za-z0-9_\-\.\:]/', '', $value );

			if ( preg_match( '/^(g|ua|aw|dc)-/i', $value ) ) {
				$value = strtoupper( $value );
			}

			return $value;
		}

		/**
		 * Retrieve the config for the plugin that owns the menu page.
		 *
		 * @return array|null
		 */
		protected static function get_menu_plugin_config() {
			if ( empty( self::$menu_plugin ) ) {
				return null;
			}

			if ( ! isset( self::$registered_plugins[ self::$menu_plugin ] ) ) {
				return null;
			}

			return self::$registered_plugins[ self::$menu_plugin ];
		}

		/**
		 * Determine requested plugin scope from query string.
		 *
		 * @return string|null
		 */
		protected static function get_requested_plugin_scope() {
			if ( empty( $_GET['plugin_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				return null;
			}

			$plugin_id = sanitize_key( wp_unslash( $_GET['plugin_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended

			if ( empty( $plugin_id ) || ! isset( self::$registered_plugins[ $plugin_id ] ) ) {
				return null;
			}

			return $plugin_id;
		}

		/**
		 * Determine selected range.
		 *
		 * @return int
		 */
		protected static function get_requested_range_days() {
			if ( empty( $_GET['range'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				return 7;
			}

			$range = (int) sanitize_text_field( wp_unslash( $_GET['range'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended

			if ( ! in_array( $range, array( 7, 14, 30, 60 ), true ) ) {
				return 7;
			}

			return $range;
		}

		/**
		 * Generate deterministic signature for plugin/site combo.
		 *
		 * @param string $plugin_id Plugin identifier.
		 * @return string
		 */
		protected static function generate_signature( $plugin_id ) {
			$site    = home_url( '/' );
			$message = 'ar-analytics|' . $plugin_id . '|' . $site;

			if ( function_exists( 'wp_hash' ) ) {
				return wp_hash( $message );
			}

			$secret = self::get_fallback_secret();

			return hash_hmac( 'sha256', $message, $secret );
		}

		/**
		 * Retrieve a secret for signing when wp_hash/wp_salt are unavailable.
		 *
		 * @return string
		 */
		protected static function get_fallback_secret() {
			if ( function_exists( 'wp_salt' ) ) {
				return wp_salt( 'ar-analytics' );
			}

			$candidates = array(
				'SECURE_AUTH_KEY',
				'AUTH_KEY',
				'LOGGED_IN_KEY',
				'NONCE_KEY',
			);

			foreach ( $candidates as $constant ) {
				if ( defined( $constant ) && constant( $constant ) ) {
					return constant( $constant );
				}
			}

			return md5( __FILE__ );
		}

		/**
		 * Get signature for plugin.
		 *
		 * @param string $plugin_id Plugin identifier.
		 * @return string
		 */
		protected static function get_signature_for_plugin( $plugin_id ) {
			if ( isset( self::$plugin_signatures[ $plugin_id ] ) ) {
				return self::$plugin_signatures[ $plugin_id ];
			}
			$signature = self::generate_signature( $plugin_id );
			self::$plugin_signatures[ $plugin_id ] = $signature;
			return $signature;
		}

		/**
		 * Register the WordPress dashboard widget.
		 *
		 * @return void
		 */
		public static function register_dashboard_widget() {
			if ( self::$dashboard_widget_registered ) {
				return;
			}

			if ( ! current_user_can( 'manage_options' ) ) {
				return;
			}

			$domain = self::get_primary_text_domain();

			wp_add_dashboard_widget(
				'ar_insights_snapshot',
				__( 'AR Insights Snapshot', $domain ),
				array( __CLASS__, 'render_dashboard_widget' )
			);

			self::$dashboard_widget_registered = true;
		}

		/**
		 * Render the dashboard widget.
		 *
		 * @return void
		 */
		public static function render_dashboard_widget() {
			$domain = self::get_primary_text_domain();

			$range_days = (int) apply_filters( 'ar_analytics_dashboard_widget_range', 7 );
			$range_days = max( 1, $range_days );

			$default_counts = array(
				'total'          => 0,
				'viewer_load'    => 0,
				'ar_launch'      => 0,
				'variant_change' => 0,
			);
			$overall_counts = $default_counts;
			$aggregated_models = array();
			$plugin_rows = array();
			foreach ( self::$registered_plugins as $plugin_id => $config ) {
				$summary = self::get_summary(
					array(
						'plugin_id' => $plugin_id,
						'days'      => $range_days,
						'limit'     => 3,
					)
				);

				$counts = isset( $summary['counts'] ) && is_array( $summary['counts'] )
					? wp_parse_args( $summary['counts'], $default_counts )
					: $default_counts;

				$overall_counts['total']          += $counts['total'];
				$overall_counts['viewer_load']    += $counts['viewer_load'];
				$overall_counts['ar_launch']      += $counts['ar_launch'];
				$overall_counts['variant_change'] += $counts['variant_change'];

				$plugin_rows[] = array(
					'plugin_id'      => $plugin_id,
					'label'          => ! empty( $config['plugin_name'] ) ? $config['plugin_name'] : $plugin_id,
					'total'          => $counts['total'],
					'viewer_load'    => $counts['viewer_load'],
					'ar_launch'      => $counts['ar_launch'],
					'variant_change' => $counts['variant_change'],
					'config'         => $config,
				);

				if ( ! empty( $summary['top_models'] ) && is_array( $summary['top_models'] ) ) {
					foreach ( $summary['top_models'] as $model ) {
						$model_id_raw = isset( $model['id'] ) ? (string) $model['id'] : '';
						$key          = $model_id_raw ? $model_id_raw : ( isset( $model['title'] ) && $model['title'] ? 'title:' . strtolower( $model['title'] ) : md5( wp_json_encode( $model ) ) );

						if ( ! isset( $aggregated_models[ $key ] ) ) {
							$aggregated_models[ $key ] = array(
								'id'    => $model_id_raw,
								'title' => isset( $model['title'] ) ? $model['title'] : '',
								'count' => 0,
							);
						}

						if ( empty( $aggregated_models[ $key ]['title'] ) && ! empty( $model['title'] ) ) {
							$aggregated_models[ $key ]['title'] = $model['title'];
						}

						$aggregated_models[ $key ]['count'] += isset( $model['count'] ) ? (int) $model['count'] : 0;
					}
				}
			}

			if ( empty( $plugin_rows ) ) {
				$overall_summary = self::get_summary(
					array(
						'plugin_id' => null,
						'days'      => $range_days,
						'limit'     => 3,
					)
				);

				$overall_counts = isset( $overall_summary['counts'] ) && is_array( $overall_summary['counts'] )
					? wp_parse_args( $overall_summary['counts'], $default_counts )
					: $default_counts;

				$plugin_rows[] = array(
					'plugin_id'      => null,
					'label'          => __( 'AR Analytics', $domain ),
					'total'          => $overall_counts['total'],
					'viewer_load'    => $overall_counts['viewer_load'],
					'ar_launch'      => $overall_counts['ar_launch'],
					'variant_change' => $overall_counts['variant_change'],
					'config'         => self::get_menu_plugin_config(),
				);

				if ( ! empty( $overall_summary['top_models'] ) && is_array( $overall_summary['top_models'] ) ) {
					foreach ( $overall_summary['top_models'] as $model ) {
						$model_id_raw = isset( $model['id'] ) ? (string) $model['id'] : '';
						$key          = $model_id_raw ? $model_id_raw : ( isset( $model['title'] ) && $model['title'] ? 'title:' . strtolower( $model['title'] ) : md5( wp_json_encode( $model ) ) );

						if ( ! isset( $aggregated_models[ $key ] ) ) {
							$aggregated_models[ $key ] = array(
								'id'    => $model_id_raw,
								'title' => isset( $model['title'] ) ? $model['title'] : '',
								'count' => 0,
							);
						}

						if ( empty( $aggregated_models[ $key ]['title'] ) && ! empty( $model['title'] ) ) {
							$aggregated_models[ $key ]['title'] = $model['title'];
						}

						$aggregated_models[ $key ]['count'] += isset( $model['count'] ) ? (int) $model['count'] : 0;
					}
				}
			}

			$top_models = array();
			if ( ! empty( $aggregated_models ) ) {
				uasort(
					$aggregated_models,
					function ( $a, $b ) {
						$a_count = isset( $a['count'] ) ? (int) $a['count'] : 0;
						$b_count = isset( $b['count'] ) ? (int) $b['count'] : 0;

						if ( $a_count === $b_count ) {
							return 0;
						}

						return ( $a_count > $b_count ) ? -1 : 1;
					}
				);

				$top_models = array_slice( array_values( $aggregated_models ), 0, 3 );
			}

			$primary_config = self::get_menu_plugin_config();
			if ( empty( $primary_config ) && ! empty( self::$registered_plugins ) ) {
				$primary_config = reset( self::$registered_plugins );
			}

			$insights_url = self::build_dashboard_insights_url( $primary_config, null );
			?>
			<style>
				.ar-dashboard-insights .ar-insights-metrics {
					display: flex;
					flex-wrap: wrap;
					gap: 12px;
					margin: 0 0 12px;
					padding: 0;
					list-style: none;
				}
				.ar-dashboard-insights .ar-insights-metrics li {
					flex: 1 1 140px;
					background: #f6f7f7;
					border-radius: 4px;
					padding: 12px;
					min-width: 120px;
				}
				.ar-dashboard-insights .ar-insights-metrics .metric-label {
					display: block;
					font-size: 12px;
					color: #50575e;
					text-transform: uppercase;
					letter-spacing: .04em;
				}
				.ar-dashboard-insights .ar-insights-metrics .metric-value {
					display: block;
					font-size: 20px;
					font-weight: 600;
					margin-top: 4px;
					color: #1d2327;
				}
				.ar-dashboard-insights table.widefat {
					margin-top: 12px;
				}
				.ar-dashboard-insights .ar-top-list {
					margin: 12px 0;
				}
				.ar-dashboard-insights .ar-top-list li {
					margin: 0;
				}
			</style>
			<div class="ar-dashboard-insights">
			<div style="display: flex; align-items: flex-start; margin-bottom: 10px;">
				<div style="flex: 0 0 auto; position: relative;">
					<img src="<?php echo plugin_dir_url( dirname( __DIR__ ) ) . 'ar-for-wordpress/assets/images/Ar_logo.png'; ?>"  alt="AR Logo" style="height: 100px;" />
				</div>
				<?php if ( $insights_url ) : ?>
					<a href="<?php echo esc_url( $insights_url ); ?>" class="ar-insights-icon-btn" title="<?php esc_attr_e( 'Open AR Insights', $domain ); ?>" style="margin-left: auto; margin-right: 0; margin-top: 10px; text-decoration: none; background: #2271b1; border-radius: 50%; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;">
						<span class="dashicons dashicons-chart-bar" style="color: #fff; background: #2271b1; border-radius: 50%; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; font-size: 24px;"></span>
					</a>
				<?php endif; ?>
			</div>
			<p style="margin-top: 0;">
				<?php
				printf(
					esc_html__( 'Tracking the last %s days of AR activity.', $domain ),
					number_format_i18n( $range_days )
				);
				?>
			</p>

				<ul class="ar-insights-metrics">
					<li>
						<span class="metric-label"><?php esc_html_e( 'Total events', $domain ); ?></span>
						<span class="metric-value"><?php echo esc_html( number_format_i18n( $overall_counts['total'] ) ); ?></span>
					</li>
					<li>
						<span class="metric-label"><?php esc_html_e( 'Model views', $domain ); ?></span>
						<span class="metric-value"><?php echo esc_html( number_format_i18n( $overall_counts['viewer_load'] ) ); ?></span>
					</li>
					<li>
						<span class="metric-label"><?php esc_html_e( 'AR launches', $domain ); ?></span>
						<span class="metric-value"><?php echo esc_html( number_format_i18n( $overall_counts['ar_launch'] ) ); ?></span>
					</li>
					<li>
						<span class="metric-label"><?php esc_html_e( 'Variant changes', $domain ); ?></span>
						<span class="metric-value"><?php echo esc_html( number_format_i18n( $overall_counts['variant_change'] ) ); ?></span>
					</li>
				</ul>

				<table class="widefat striped">
					<thead>
						<tr>
							<th scope="col"><?php esc_html_e( 'Plugin', $domain ); ?></th>
							<th scope="col" class="column-events"><?php esc_html_e( 'Events', $domain ); ?></th>
							<th scope="col" class="column-ar-launches"><?php esc_html_e( 'AR Launches', $domain ); ?></th>
							<th scope="col" class="column-variant-changes"><?php esc_html_e( 'Variant Changes', $domain ); ?></th>
						</tr>
					</thead>
					<tbody>
						<?php foreach ( $plugin_rows as $row ) : ?>
							<tr>
								<td>
									<?php
									$link = self::build_dashboard_insights_url( $row['config'], $row['plugin_id'] );
									if ( $link ) {
										echo '<a href="' . esc_url( $link ) . '">' . esc_html( $row['label'] ) . '</a>';
									} else {
										echo esc_html( $row['label'] );
									}
									?>
								</td>
								<td class="column-events"><?php echo esc_html( number_format_i18n( $row['total'] ) ); ?></td>
								<td class="column-ar-launches"><?php echo esc_html( number_format_i18n( $row['ar_launch'] ) ); ?></td>
								<td class="column-variant-changes"><?php echo esc_html( number_format_i18n( $row['variant_change'] ) ); ?></td>
							</tr>
						<?php endforeach; ?>
					</tbody>
				</table>

				<p><b><?php esc_html_e( 'Top models', $domain ); ?></b></p>
				<?php if ( ! empty( $top_models ) ) : ?>
					<table class="widefat striped ar-top-models">
						<thead>
							<tr>
								<th scope="col"><?php esc_html_e( 'Model', $domain ); ?></th>
								<th scope="col" class="column-events"><?php esc_html_e( 'Events', $domain ); ?></th>
							</tr>
						</thead>
						<tbody>
							<?php foreach ( $top_models as $model ) : ?>
								<tr>
									<td>
										<?php
										$model_id_raw = isset( $model['id'] ) ? $model['id'] : '';
										$model_id     = is_numeric( $model_id_raw ) ? (int) $model_id_raw : $model_id_raw;
										$title        = '';

										if ( isset( $model['title'] ) && $model['title'] ) {
											$title = $model['title'];
										} elseif ( $model_id ) {
											$title = is_numeric( $model_id ) ? sprintf( __( 'Model #%d', $domain ), $model_id ) : $model_id;
										} else {
											$title = __( 'Unknown', $domain );
										}

										$edit_link = '';
										if ( $model_id && is_numeric( $model_id ) ) {
											$edit_link = get_edit_post_link( (int) $model_id );
										}

										if ( $edit_link ) {
											echo '<a href="' . esc_url( $edit_link ) . '">' . esc_html( $title ) . '</a>';
										} else {
											echo esc_html( $title );
										}
										?>
									</td>
									<td class="column-events">
										<?php
										$count = isset( $model['count'] ) ? (int) $model['count'] : 0;
										echo esc_html( number_format_i18n( $count ) );
										?>
									</td>
								</tr>
							<?php endforeach; ?>
						</tbody>
					</table>
				<?php else : ?>
					<p class="ar-no-top-models"><?php esc_html_e( 'No recent models have recorded analytics yet.', $domain ); ?></p>
				<?php endif; ?>
			</div>
			<?php
		}

		/**
		 * Retrieve the primary text domain for translations.
		 *
		 * @return string
		 */
		protected static function get_primary_text_domain() {
			$config = self::get_menu_plugin_config();

			if ( ! empty( $config['text_domain'] ) ) {
				return $config['text_domain'];
			}

			if ( ! empty( self::$registered_plugins ) ) {
				$first = reset( self::$registered_plugins );
				if ( ! empty( $first['text_domain'] ) ) {
					return $first['text_domain'];
				}
			}

			return 'ar-for-wordpress';
		}

		/**
		 * Build an insights URL for a given plugin config.
		 *
		 * @param array      $config    Plugin configuration.
		 * @param string|nil $plugin_id Plugin identifier.
		 * @return string|null
		 */
		protected static function build_dashboard_insights_url( $config, $plugin_id ) {
			if ( empty( $config ) || ! is_array( $config ) ) {
				$config = self::get_menu_plugin_config();
			}

			if ( empty( $config ) || ! is_array( $config ) ) {
				return null;
			}

			$menu_slug = isset( $config['menu_slug'] ) ? $config['menu_slug'] : 'ar-analytics';
			$url       = menu_page_url( $menu_slug, false );

			if ( empty( $url ) ) {
				$url = admin_url( 'admin.php?page=' . $menu_slug );
			}

			if ( $plugin_id ) {
				$url = add_query_arg( 'plugin_id', $plugin_id, $url );
			}

			return $url;
		}


		/**
		 * Extract plugin id from request payload.
		 *
		 * @param array|null $json_params JSON params.
		 * @return string
		 */
		protected static function extract_plugin_id_from_request( $json_params ) {
			if ( empty( $json_params ) || ! is_array( $json_params ) ) {
				return '';
			}

			if ( ! empty( $json_params['pluginId'] ) ) {
				return self::normalize_plugin_id( $json_params['pluginId'] );
			}

			if ( ! empty( $json_params['events'] ) && is_array( $json_params['events'] ) ) {
				$first = reset( $json_params['events'] );
				if ( isset( $first['pluginId'] ) ) {
					return self::normalize_plugin_id( $first['pluginId'] );
				}
			}

			return '';
		}

		/**
		 * Extract signature from headers or body.
		 *
		 * @param WP_REST_Request $request Request.
		 * @param array|null      $json_params JSON params.
		 * @return string
		 */
		protected static function extract_signature_from_request( WP_REST_Request $request, $json_params ) {
			$signature = $request->get_header( 'x_ar_signature' );

			if ( empty( $signature ) ) {
				$signature = $request->get_param( 'signature' );
			}

			if ( empty( $signature ) && ! empty( $json_params['signature'] ) ) {
				$signature = $json_params['signature'];
			}

			if ( empty( $signature ) && ! empty( $json_params['events'] ) && is_array( $json_params['events'] ) ) {
				$first = reset( $json_params['events'] );
				if ( isset( $first['signature'] ) ) {
					$signature = $first['signature'];
				}
			}

			return is_string( $signature ) ? trim( $signature ) : '';
		}

		/**
		 * Validate provided signature.
		 *
		 * @param string $plugin_id Plugin identifier.
		 * @param string $signature Provided signature.
		 * @return bool
		 */
		protected static function verify_signature( $plugin_id, $signature ) {
			if ( empty( $plugin_id ) || empty( $signature ) ) {
				return false;
			}

			$plugin_id = self::normalize_plugin_id( $plugin_id );
			$expected = self::get_signature_for_plugin( $plugin_id );
			return hash_equals( $expected, $signature );
		}

		/**
		 * Normalise plugin identifiers.
		 *
		 * @param string $plugin_id Raw plugin id.
		 * @return string
		 */
		protected static function normalize_plugin_id( $plugin_id ) {
			$plugin_id = strtolower( trim( (string) $plugin_id ) );
			$plugin_id = preg_replace( '/[^a-z0-9_\-]/', '', $plugin_id );
			return $plugin_id;
		}

		/**
		 * Log permission failures when debugging.
		 *
		 * @param array $context Context.
		 * @return void
		 */
		protected static function log_permission_failure( array $context ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( '[AR Analytics] Permission denied: ' . wp_json_encode( $context ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
			}
		}

		/**
		 * Determine if request originates from same site.
		 *
		 * @param WP_REST_Request $request Request.
		 * @return bool
		 */
		protected static function is_same_origin_request( WP_REST_Request $request ) {
			$home       = trailingslashit( home_url() );
			$origin     = $request->get_header( 'origin' );
			$referer    = $request->get_header( 'referer' );
			$candidates = array_filter( array( $origin, $referer ) );

			foreach ( $candidates as $candidate ) {
				if ( empty( $candidate ) ) {
					continue;
				}
				$candidate = trailingslashit( $candidate );
				if ( 0 === strpos( $candidate, $home ) ) {
					return true;
				}
			}

			return false;
		}
	}
}

if ( ! function_exists( 'ar_get_analytics_context' ) ) {
	/**
	 * Helper to build analytics context for scripts/templates.
	 *
	 * @param array $extra Extra data.
	 * @return array
	 */
	function ar_get_analytics_context( array $extra = array() ) {
		if ( class_exists( 'AR_Plugin_Analytics' ) ) {
			return AR_Plugin_Analytics::build_context( $extra );
		}

		return array();
	}
}

if ( ! function_exists( 'ar_get_analytics_localized_settings' ) ) {
	/**
	 * Retrieve localized analytics settings for the current plugin.
	 *
	 * @param string|null $plugin_id Plugin ID override.
	 * @return array|null
	 */
	function ar_get_analytics_localized_settings( $plugin_id = null ) {
		if ( null === $plugin_id ) {
			global $ar_plugin_id;
			$plugin_id = $ar_plugin_id;
		}

		if ( empty( $plugin_id ) || ! class_exists( 'AR_Plugin_Analytics' ) ) {
			return null;
		}

		return AR_Plugin_Analytics::get_script_settings_for_plugin( $plugin_id );
	}
}

if ( ! function_exists( 'ar_for_wordpress_boot_analytics' ) ) {
	function ar_for_wordpress_boot_analytics() {
		if ( ! function_exists( 'get_file_data' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		$plugin_dir  = dirname( __FILE__, 2 );
		$plugin_file = $plugin_dir . '/ar-wordpress.php';
		$plugin_url  = plugin_dir_url( $plugin_file );
		$plugin_data = get_file_data( $plugin_file, array( 'Version' => 'Version' ), false );
		$version     = ! empty( $plugin_data['Version'] ) ? $plugin_data['Version'] : '8.32';

		AR_Plugin_Analytics::boot(
			array(
				'plugin_id'       => 'ar-for-wordpress',
				'plugin_name'     => 'AR for WordPress',
				'text_domain'     => 'ar-for-wordpress',
				'plugin_root_url' => $plugin_url,
				'version'         => $version,
				'register_menu'   => true,
				'menu_slug'       => 'ar-analytics',
				'menu_title'      => __( 'AR Insights', 'ar-for-wordpress' ),
				'page_title'      => __( 'AR Insights Dashboard', 'ar-for-wordpress' ),
				'parent_slug'     => 'edit.php?post_type=armodels',
			)
		);
	}

	add_action( 'init', 'ar_for_wordpress_boot_analytics', 5 );
}

