defined( 'AICOMMERCE_VERSION' ) ? AICOMMERCE_VERSION : 'unknown', 'db_version' => get_option( 'aicommerce_db_version', 'unknown' ), 'wp_version' => get_bloginfo( 'version' ), 'wc_version' => defined( 'WC_VERSION' ) ? WC_VERSION : 'unknown', 'php_version' => PHP_VERSION, 'multisite' => is_multisite(), 'active_modules' => $this->get_active_modules(), 'total_products' => $this->get_product_count(), 'schema_enabled' => (bool) get_option( 'aicommerce_schema_enabled', true ), 'feeds_configured' => $this->get_feed_count(), 'mcp_tools_count' => 8, 'mcp_api_keys' => count( get_option( 'aicommerce_mcp_api_keys', array() ) ), 'stripe_acp_enabled' => (bool) get_option( 'aicommerce_stripe_acp_enabled', false ), 'agent_sessions_total' => $this->get_agent_session_count(), 'agent_sessions_30d' => $this->get_agent_session_count( 30 ), 'soa_last_run' => get_option( 'aicommerce_soa_last_run', null ), 'competitors_tracked' => $this->get_competitor_count(), 'addons_registered' => \AICommerce\Core\AddonRegistry::count(), 'edition' => 'free', 'ai_keys_configured' => $this->get_configured_ai_keys(), 'score_current' => $this->get_current_score(), 'crawler_visits_30d' => $this->get_crawler_visits( 30 ), 'attribution_revenue_30d' => $this->get_attribution_revenue( 30 ), 'generated_at' => gmdate( 'c' ), ); } /** * Handle REST request for metrics export. * * @return \WP_REST_Response */ public function handle_export(): \WP_REST_Response { if ( get_transient( self::RATE_LIMIT_KEY ) ) { return new \WP_REST_Response( array( 'error' => 'Rate limited. One request per hour.' ), 429 ); } set_transient( self::RATE_LIMIT_KEY, 1, HOUR_IN_SECONDS ); return new \WP_REST_Response( $this->export_plugin_metrics(), 200 ); } /** * Get active module list. * * @return string[] */ private function get_active_modules(): array { $modules = array( 'schema', 'feeds', 'crawler', 'content', 'geo_dashboard', 'agentic' ); return $modules; } /** * Get total published product count. * * @return int */ private function get_product_count(): int { if ( ! post_type_exists( 'product' ) ) { return 0; } $counts = wp_count_posts( 'product' ); return ( $counts && isset( $counts->publish ) ) ? (int) $counts->publish : 0; } /** * Get count of configured feeds. * * @return int */ private function get_feed_count(): int { $feeds = get_option( 'aicommerce_feed_types', array() ); return is_array( $feeds ) ? count( $feeds ) : 0; } /** * Get agent session count. * * @param int|null $days Limit to last N days, or null for all time. * * @return int */ private function get_agent_session_count( ?int $days = null ): int { global $wpdb; $where = ''; if ( $days ) { // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date $since = date( 'Y-m-d', time() - ( $days * DAY_IN_SECONDS ) ); $where = $wpdb->prepare( ' WHERE created_at >= %s', $since . ' 00:00:00' ); } // Safe: internal $wpdb->prefix table name; $where is built with $wpdb->prepare(). // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}aicommerce_agent_sessions{$where}" ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter } /** * Get competitor count. * * @return int */ private function get_competitor_count(): int { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}aicommerce_soa_competitors WHERE is_active = 1" ); } /** * Get list of configured AI API keys. * * @return string[] Provider slugs with keys set. */ private function get_configured_ai_keys(): array { $keys = array(); foreach ( array( 'openai', 'anthropic', 'gemini' ) as $provider ) { if ( get_option( "aicommerce_{$provider}_api_key" ) ) { $keys[] = $provider; } } return $keys; } /** * Get current AI Readiness Score. * * @return int|null */ private function get_current_score(): ?int { $cached = get_transient( 'aicommerce_readiness_score_v2' ); return is_array( $cached ) ? (int) ( $cached['overall'] ?? 0 ) : null; } /** * Get crawler visit count for last N days. * * @param int $days Number of days. * * @return int */ private function get_crawler_visits( int $days ): int { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}aicommerce_crawler_log WHERE request_time >= %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared gmdate( 'Y-m-d H:i:s', time() - ( $days * DAY_IN_SECONDS ) ) ) ); } /** * Get AI-attributed revenue for last N days. * * @param int $days Number of days. * * @return float */ private function get_attribution_revenue( int $days ): float { global $wpdb; // Safe: internal $wpdb->prefix table names; user values use placeholders. // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter $revenue = $wpdb->get_var( $wpdb->prepare( "SELECT COALESCE(SUM(revenue), 0) FROM {$wpdb->prefix}aicommerce_attribution WHERE attributed_at >= %s", gmdate( 'Y-m-d H:i:s', time() - ( $days * DAY_IN_SECONDS ) ) ) ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter return (float) $revenue; } }