'POST', 'callback' => array($this, 'handle_search_request'), 'permission_callback' => function() { if (!is_user_logged_in()) { return new WP_Error('aipa_auth_required', __('Please log in to use AI search feature.', 'ai-product-assistant'), array('status' => 401) ); } return true; }, 'args' => array( 'query' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ), 'batch_offset' => array( 'default' => 0, 'sanitize_callback' => 'absint', ), 'deep_search' => array( 'default' => false, ), 'search_session_key' => array( 'default' => '', 'sanitize_callback' => 'sanitize_text_field', ), ), )); register_rest_route('aipa/v1', '/cart-analysis', array( 'methods' => 'POST', 'callback' => array($this, 'handle_cart_analysis_request'), 'permission_callback' => function() { if (!is_user_logged_in()) { return new WP_Error('aipa_auth_required', __('Please log in to use AI cart analysis feature.', 'ai-product-assistant'), array('status' => 401) ); } return true; }, )); register_rest_route('aipa/v1', '/product-assistant', array( 'methods' => 'POST', 'callback' => array($this, 'handle_product_assistant_request'), 'permission_callback' => function() { if (!is_user_logged_in()) { return new WP_Error('aipa_auth_required', __('Please log in to use AI product assistant feature.', 'ai-product-assistant'), array('status' => 401) ); } return true; }, 'args' => array( 'query' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ), 'product_data' => array( 'required' => true, ), ), )); } /** * Handle product assistant request */ public function handle_product_assistant_request($request) { $start_time = microtime(true); try { $query = sanitize_text_field($request->get_param('query')); $product_data = $request->get_param('product_data'); // Debug logging AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Product_Assistant_API', 'Received product data', array( 'query' => $query, 'product_id' => isset($product_data['id']) ? $product_data['id'] : 'not_set' )); if (empty($query)) { return new WP_Error('invalid_query', __('Please provide a question.', 'ai-product-assistant'), array('status' => 400)); } if (empty($product_data) || !isset($product_data['id'])) { return new WP_Error('invalid_product_data', __('Invalid product data.', 'ai-product-assistant'), array('status' => 400)); } $options = get_option('aipa_settings'); $prompt_prefix = isset($options['prompt_prefix']) ? $options['prompt_prefix'] : ''; $prompt_suffix = isset($options['prompt_suffix']) ? $options['prompt_suffix'] : ''; $full_query = $prompt_prefix . ' ' . $query . ' ' . $prompt_suffix; // Call AI API with error handling $ai_results = $this->call_ai_api_for_product_assistance($full_query, $product_data, $options); if (is_wp_error($ai_results)) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Product_Assistant_API', 'AI API call failed', array( 'error_code' => $ai_results->get_error_code(), 'error_message' => $ai_results->get_error_message() )); return $ai_results; } // Validate AI results structure if (!is_array($ai_results)) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Product_Assistant_API', 'AI results not an array', array( 'type' => gettype($ai_results) )); return new WP_Error('invalid_ai_response', __('Invalid response format from AI.', 'ai-product-assistant'), array('status' => 500)); } $execution_time = microtime(true) - $start_time; // Save statistics with error handling try { $this->save_statistics( 'product_assistant', $options, $full_query, isset($ai_results['raw_response']) ? $ai_results['raw_response'] : json_encode($ai_results), $ai_results['request_tokens'] ?? 0, $ai_results['response_tokens'] ?? 0, $execution_time ); } catch (Exception $e) { AIPA_Debug::log(AIPA_Debug::LEVEL_WARNING, 'Product_Assistant_API', 'Failed to save statistics', array( 'error' => $e->getMessage() )); // Don't fail the request if statistics saving fails } // Remove raw response to reduce payload size if (isset($ai_results['raw_response'])) { unset($ai_results['raw_response']); } // Ensure the response can be JSON encoded $json_test = json_encode($ai_results); if ($json_test === false) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Product_Assistant_API', 'JSON encoding failed for response', array( 'json_error' => json_last_error(), 'json_error_msg' => json_last_error_msg() )); // Return error instead of trying to clean since this is mock data return new WP_Error('json_encode_error', __('Failed to encode response.', 'ai-product-assistant'), array('status' => 500)); } AIPA_Debug::log(AIPA_Debug::LEVEL_INFO, 'Product_Assistant_API', 'Request completed successfully', array( 'execution_time' => $execution_time, 'has_analysis' => isset($ai_results['analysis']), 'has_features' => isset($ai_results['features']), 'has_recommendations' => isset($ai_results['recommendations']) )); return rest_ensure_response($ai_results); } catch (Exception $e) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Product_Assistant_API', 'Exception in handle_product_assistant_request', array( 'exception' => $e->getMessage(), 'trace' => $e->getTraceAsString() )); return new WP_Error('aipa_service_error', $this->get_ai_service_error_message(), array('status' => 500)); } } /** * Call AI API for product assistance - MOCK IMPLEMENTATION */ protected function call_ai_api_for_product_assistance($query, $product_data, $options) { $api_provider = isset($options['api_provider']) ? $options['api_provider'] : 'unknown'; AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Mock_API', 'Entering call_ai_api_for_product_assistance (MOCK)', array( 'query_length' => strlen($query), 'product_id' => isset($product_data['id']) ? $product_data['id'] : 'unknown', 'product_name' => isset($product_data['name']) ? $product_data['name'] : 'unknown', 'configured_provider' => $api_provider )); // MOCK IMPLEMENTATION $mock_analysis = sprintf( /* translators: 1: product name, 2: user question */ __('This is a mock analysis for the %1$s. It demonstrates how the AI would analyze your product based on the question: "%2$s". In the Pro version, this would provide real AI-powered insights about features, benefits, and recommendations.', 'ai-product-assistant'), $product_data['name'] ?? 'product', $query ); $mock_features = array( __('Feature 1: High-quality materials', 'ai-product-assistant'), __('Feature 2: Excellent performance', 'ai-product-assistant'), __('Feature 3: Great value for money', 'ai-product-assistant'), ); $mock_recommendations = array( __('Recommendation 1: Perfect for daily use', 'ai-product-assistant'), __('Recommendation 2: Suitable for beginners and experts', 'ai-product-assistant'), __('Recommendation 3: Consider purchasing with complementary products', 'ai-product-assistant'), ); $result = array( 'analysis' => $mock_analysis, 'features' => $mock_features, 'recommendations' => $mock_recommendations, 'request_tokens' => 100, 'response_tokens' => 150, 'raw_response' => json_encode(array( 'mock' => true, 'message' => __('This is a mock response. Upgrade to Pro for real AI analysis.', 'ai-product-assistant') )) ); AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Mock_API', 'Exiting call_ai_api_for_product_assistance (MOCK)', array( 'analysis_length' => strlen($mock_analysis), 'features_count' => count($mock_features), 'recommendations_count' => count($mock_recommendations), 'configured_provider' => $api_provider, 'tokens' => array( 'request' => 100, 'response' => 150 ) )); return $result; } /** * Handle product search request - MOCK IMPLEMENTATION */ public function handle_search_request($request) { try { // Start timing for statistics $start_time = microtime(true); AIPA_Debug::log(AIPA_Debug::LEVEL_INFO, 'Search_API', 'Search request received', array('timestamp' => gmdate('Y-m-d H:i:s'))); // Get query and batch parameters $query = sanitize_text_field($request->get_param('query')); $batch_offset = intval($request->get_param('batch_offset') ?? 0); $deep_search = filter_var($request->get_param('deep_search'), FILTER_VALIDATE_BOOLEAN); if (empty($query)) { AIPA_Debug::log(AIPA_Debug::LEVEL_WARNING, 'Search_API', 'Empty query provided'); return new WP_Error('invalid_query', __('Please provide a search query.', 'ai-product-assistant'), array('status' => 400)); } // Get settings $options = get_option('aipa_settings'); $batch_size = isset($options['search_batch_size']) ? intval($options['search_batch_size']) : 100; // Get max batches with dynamic default calculation if (isset($options['max_batches'])) { $max_batches = intval($options['max_batches']); } else { $product_count = $this->get_total_products_count(); $max_batches = $product_count > 0 ? ceil($product_count / $batch_size) : 1; } $enable_deep_search = isset($options['enable_deep_search']) ? $options['enable_deep_search'] : true; // If deep search is disabled in settings, override the request if (!$enable_deep_search) { $deep_search = false; } // Check for valid batch offset - strict check of batch range if ($batch_offset >= $max_batches) { AIPA_Debug::log(AIPA_Debug::LEVEL_WARNING, 'Search_API', 'Maximum batch limit reached', array('batch_offset' => $batch_offset, 'max_batches' => $max_batches)); return new WP_Error('invalid_batch', __('Maximum batch limit reached.', 'ai-product-assistant'), array('status' => 400)); } // Add prefix and suffix to query if set $prompt_prefix = isset($options['prompt_prefix']) ? $options['prompt_prefix'] : ''; $prompt_suffix = isset($options['prompt_suffix']) ? $options['prompt_suffix'] : ''; $full_query = $prompt_prefix . ' ' . $query . ' ' . $prompt_suffix; $total_products = $this->get_total_products_count(); $products_data = $this->get_products_batch($options, $query, $batch_size, $batch_offset); // Skip AI call if batch is empty (no products in this offset range) if (empty($products_data)) { AIPA_Debug::log(AIPA_Debug::LEVEL_INFO, 'Search_API', 'Empty product batch, skipping AI call', array('batch_offset' => $batch_offset)); $ai_results = array('product_ids' => array(), 'explanations' => array(), 'request_tokens' => 0, 'response_tokens' => 0); } else { // MOCK AI RESULTS - Return random product IDs with mock explanations $ai_results = $this->call_ai_api_for_search($full_query, $products_data, $options); if (is_wp_error($ai_results)) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Search_API', 'AI API call failed', array('error' => $ai_results->get_error_message())); return $ai_results; } } // Current batch number = batch_offset + 1 (show to user starting from 1) $display_batch = $batch_offset + 1; // Progress percentage calculation - assign average progress per batch $progress_percentage = min(100, $display_batch * (100 / $max_batches)); // Whether there are more batches - check if current batch is less than max batch $has_more_batches = $deep_search && $display_batch < $max_batches; $ai_results['meta'] = array( 'batch_size' => $batch_size, 'batch_offset' => $batch_offset, // Original batch offset (starts from 0) 'current_batch' => $display_batch, // Display batch for user (starts from 1) 'max_batches' => $max_batches, 'total_products' => $total_products, 'progress' => $progress_percentage, 'has_more_batches' => $has_more_batches, 'next_batch_offset' => $batch_offset + 1 // Next batch offset ); // Calculate execution time $execution_time = microtime(true) - $start_time; AIPA_Debug::log(AIPA_Debug::LEVEL_INFO, 'Search_API', 'Search request completed', array( 'execution_time' => $execution_time, 'products_found' => count($ai_results['product_ids']), 'batch_offset' => $batch_offset, 'has_more_batches' => $has_more_batches )); // Save statistics - skip empty batches (no AI call was made) if (empty($products_data)) { return rest_ensure_response($ai_results); } $this->save_statistics( 'search', $options, $full_query, isset($ai_results['raw_response']) ? $ai_results['raw_response'] : json_encode($ai_results), $ai_results['request_tokens'] ?? 0, $ai_results['response_tokens'] ?? 0, $execution_time ); // Update usage statistics for review notice $usage_stats = get_option('aipa_usage_stats', array('search_count' => 0)); $usage_stats['search_count']++; update_option('aipa_usage_stats', $usage_stats); // Remove raw response if (isset($ai_results['raw_response'])) { unset($ai_results['raw_response']); } return rest_ensure_response($ai_results); } catch (Exception $e) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Search_API', 'Exception in handle_search_request', array( 'exception' => $e->getMessage(), 'trace' => $e->getTraceAsString() )); return new WP_Error('aipa_service_error', $this->get_ai_service_error_message(), array('status' => 500)); } } /** * Check if currently in mock mode */ protected function is_mock_mode() { // Free version is always in mock mode if (get_class($this) === 'AIPA_API') { return true; } // Pro version - check if license is valid if (function_exists('aipa_pro_fs')) { return !aipa_pro_fs()->can_use_premium_code__premium_only(); } // Fallback - if pro functions don't exist, assume mock mode return true; } /** * Get user role for statistics * Returns user role in free version, can be overridden in Pro version * * @return string User role or identifier */ protected function get_user_role() { $user_id = get_current_user_id(); if ($user_id > 0) { $user = get_userdata($user_id); return $user && !empty($user->roles) ? $user->roles[0] : 'user'; } return 'guest'; } /** * Save statistics to database */ protected function save_statistics($request_type, $options, $request_prompt, $response_text, $request_tokens, $response_tokens, $execution_time) { global $wpdb; $table_name = $wpdb->prefix . 'aipa_statistics'; // Check if table exists (necessary system check with prepared statement) // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) != $table_name) { AIPA_Debug::log(AIPA_Debug::LEVEL_WARNING, 'Statistics', 'Statistics table does not exist', array('table_name' => $table_name)); return; } // Get user role/identifier using overridable method $user_role = $this->get_user_role(); // Get API provider and model // Check if we're in mock mode (free version or Pro without valid license) $is_mock_mode = $this->is_mock_mode(); if ($is_mock_mode) { // Mock mode - using simulated responses $api_provider = 'mock'; $api_model = 'mock'; } else { // Real AI mode - using actual API $api_provider = isset($options['api_provider']) ? $options['api_provider'] : 'unknown'; $api_model = isset($options['api_model']) ? $options['api_model'] : 'unknown'; } // Calculate character counts $request_chars = mb_strlen($request_prompt); $response_chars = mb_strlen($response_text); // Insert data (store full prompts and responses for analysis) // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $result = $wpdb->insert( $table_name, array( 'user_role' => $user_role, 'timestamp' => current_time('mysql'), 'api_provider' => $api_provider, 'api_model' => $api_model, 'request_type' => $request_type, 'request_prompt' => $request_prompt, 'request_chars' => $request_chars, 'request_tokens' => $request_tokens, 'response_text' => $response_text, 'response_chars' => $response_chars, 'response_tokens' => $response_tokens, 'execution_time' => $execution_time ) ); if ($result === false) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Statistics', 'Failed to save statistics', array('wpdb_error' => $wpdb->last_error)); } else { AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Statistics', 'Statistics saved successfully', array( 'request_type' => $request_type, 'execution_time' => $execution_time, 'tokens' => $request_tokens + $response_tokens )); } } /** * Get total product count */ protected function get_total_products_count() { $counts = wp_count_posts('product'); return isset($counts->publish) ? (int) $counts->publish : 0; } /** * Get product data for specific product IDs. * Base stub — overridden by Pro with full field support. */ protected function get_products_by_ids($product_ids, $options) { if (empty($product_ids)) return array(); $products = wc_get_products(array( 'status' => 'publish', 'include' => $product_ids, 'limit' => count($product_ids), )); $products_data = array(); foreach ($products as $product) { $products_data[] = array( 'id' => $product->get_id(), 'data' => array('title' => $product->get_name()), ); } return $products_data; } /** * Get products batch for AI analysis */ protected function get_products_batch($options, $query, $batch_size, $batch_offset) { $start_time = microtime(true); $args = array( 'status' => 'publish', 'limit' => $batch_size, 'offset' => $batch_offset * $batch_size, ); AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Product_Batch', 'Fetching product batch', array( 'batch_size' => $batch_size, 'batch_offset' => $batch_offset, 'query' => substr($query, 0, 50) . '...' )); $default_use_native_template = isset($options['use_native_template']) ? $options['use_native_template'] : true; if($default_use_native_template){ $woo_hide_out_of_stock = get_option('woocommerce_hide_out_of_stock_items', 'no'); if ($woo_hide_out_of_stock === 'yes') { $args['stock_status'] = 'instock'; } } else{ // Whether to include out of stock products if (empty($options['show_out_of_stock'])) { $args['stock_status'] = 'instock'; } } $products = wc_get_products($args); $products_data = array(); foreach ($products as $product) { $product_data = array( 'id' => $product->get_id(), 'data' => array() ); // Add product fields based on settings $search_fields = isset($options['search_fields']) ? $options['search_fields'] : array(); if (isset($search_fields['title']) && $search_fields['title']) { $product_data['data']['title'] = $product->get_name(); } if (isset($search_fields['content']) && $search_fields['content']) { $description_length_limit = isset($options['description_length_limit']) ? intval($options['description_length_limit']) : 500; $content = $product->get_description(); $product_data['data']['content'] = substr($content, 0, $description_length_limit); } if (isset($search_fields['short_description']) && $search_fields['short_description']) { $short_description_length_limit = isset($options['short_description_length_limit']) ? intval($options['short_description_length_limit']) : 200; $short_description = $product->get_short_description(); $product_data['data']['short_description'] = substr($short_description, 0, $short_description_length_limit); } if (isset($search_fields['categories']) && $search_fields['categories']) { $categories = array(); $terms = get_the_terms($product->get_id(), 'product_cat'); if ($terms && !is_wp_error($terms)) { foreach ($terms as $term) { $categories[] = $term->name; } } $product_data['data']['categories'] = implode(', ', $categories); } if (isset($search_fields['attributes']) && $search_fields['attributes']) { $attributes = array(); $product_attributes = $product->get_attributes(); if (!empty($product_attributes)) { foreach ($product_attributes as $attribute) { if ($attribute->is_taxonomy()) { $taxonomy = $attribute->get_taxonomy_object(); $attribute_name = $taxonomy->attribute_label; $values = array(); $terms = $attribute->get_terms(); if (!empty($terms)) { foreach ($terms as $term) { $values[] = $term->name; } } $attributes[$attribute_name] = implode(', ', $values); } else { $attribute_name = $attribute->get_name(); $attributes[$attribute_name] = implode(', ', $attribute->get_options()); } } } $product_data['data']['attributes'] = $attributes; } // Add regular_price if selected if (isset($search_fields['regular_price']) && $search_fields['regular_price']) { $product_data['data']['regular_price'] = $product->get_regular_price(); } // Add sale_price if selected if (isset($search_fields['sale_price']) && $search_fields['sale_price']) { $product_data['data']['sale_price'] = $product->get_sale_price(); } // Add tags if selected if (isset($search_fields['tags']) && $search_fields['tags']) { $tags = array(); $terms = get_the_terms($product->get_id(), 'product_tag'); if ($terms && !is_wp_error($terms)) { foreach ($terms as $term) { $tags[] = $term->name; } } $product_data['data']['tags'] = implode(', ', $tags); } // Add brand if selected if (isset($search_fields['brand']) && $search_fields['brand']) { $brands = array(); $terms = get_the_terms($product->get_id(), 'product_brand'); if ($terms && !is_wp_error($terms)) { foreach ($terms as $term) { $brands[] = $term->name; } } $product_data['data']['brand'] = implode(', ', $brands); } // Add weight if selected if (isset($search_fields['weight']) && $search_fields['weight']) { $weight = $product->get_weight(); if ($weight) { $weight_unit = get_option('woocommerce_weight_unit'); $product_data['data']['weight'] = $weight . ' ' . $weight_unit; } } // Add dimensions if selected if (isset($search_fields['dimensions']) && $search_fields['dimensions']) { $dimensions = array(); $length = $product->get_length(); $width = $product->get_width(); $height = $product->get_height(); if ($length || $width || $height) { $dimension_unit = get_option('woocommerce_dimension_unit'); $dimensions_text = ''; if ($length) $dimensions_text .= $length; if ($width) $dimensions_text .= ' x ' . $width; if ($height) $dimensions_text .= ' x ' . $height; $product_data['data']['dimensions'] = trim($dimensions_text, ' x ') . ' ' . $dimension_unit; } } // Add variation data if selected and product is variable if (isset($search_fields['variation_data']) && $search_fields['variation_data'] && $product->is_type('variable')) { $variations = array(); $variation_ids = $product->get_children(); foreach ($variation_ids as $variation_id) { $variation = wc_get_product($variation_id); if ($variation) { $variation_data = array( 'id' => $variation_id, 'attributes' => $variation->get_variation_attributes(), 'price' => $variation->get_price(), 'in_stock' => $variation->is_in_stock() ); $variations[] = $variation_data; } } $product_data['data']['variation_data'] = $variations; } // Add custom fields if configured if (isset($options['custom_fields']) && is_array($options['custom_fields'])) { $all_meta = get_post_meta($product->get_id()); $custom_meta = array(); foreach ($options['custom_fields'] as $custom_field) { if (isset($custom_field['meta_key'])) { $meta_key = $custom_field['meta_key']; if (isset($all_meta[$meta_key])) { $meta_value = maybe_unserialize($all_meta[$meta_key][0]); if (!empty($meta_value)) { $custom_meta[$meta_key] = $meta_value; } } } } if (!empty($custom_meta)) { $product_data['data']['custom_meta'] = $custom_meta; } } $products_data[] = $product_data; } $elapsed_time = microtime(true) - $start_time; AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Product_Batch', 'Product batch fetched', array( 'products_count' => count($products_data), 'execution_time' => $elapsed_time, 'memory_usage' => memory_get_usage(true) / 1048576 . ' MB' )); return $products_data; } /** * Handle cart analysis request - MOCK IMPLEMENTATION */ public function handle_cart_analysis_request($request) { try { // Start timing for statistics $start_time = microtime(true); // Get query from request $query = sanitize_text_field($request->get_param('query')); if (empty($query)) { return new WP_Error('invalid_query', __('Please provide an analysis query.', 'ai-product-assistant'), array('status' => 400)); } // Get settings $options = get_option('aipa_settings'); // Check if cart analysis is enabled if (empty($options['enable_cart_analysis'])) { return new WP_Error('cart_analysis_disabled', __('Cart analysis feature is disabled.', 'ai-product-assistant'), array('status' => 403)); } // Get cart items $cart_items = $this->get_cart_items($options); if (empty($cart_items)) { return new WP_Error('empty_cart', __('Your cart is empty. Add some products first.', 'ai-product-assistant'), array('status' => 400)); } // Add prefix and suffix to query if set $prompt_prefix = isset($options['prompt_prefix']) ? $options['prompt_prefix'] : ''; $prompt_suffix = isset($options['prompt_suffix']) ? $options['prompt_suffix'] : ''; $full_query = $prompt_prefix . ' ' . $query . ' ' . $prompt_suffix; // Call AI API for cart analysis - MOCK IMPLEMENTATION $ai_results = $this->call_ai_api_for_cart_analysis($full_query, $cart_items, $options); if (is_wp_error($ai_results)) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Cart_Analysis_Handler', 'AI API returned error', array( 'error_code' => $ai_results->get_error_code(), 'error_message' => $ai_results->get_error_message() )); return $ai_results; } // Validate AI results structure if (!is_array($ai_results)) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Cart_Analysis_Handler', 'AI results not an array', array( 'type' => gettype($ai_results) )); return new WP_Error('invalid_ai_response', __('Invalid response format from AI.', 'ai-product-assistant'), array('status' => 500)); } // Calculate execution time $execution_time = microtime(true) - $start_time; // Save statistics - MOCK IMPLEMENTATION $this->save_statistics( 'cart_analysis', $options, $full_query, isset($ai_results['raw_response']) ? $ai_results['raw_response'] : json_encode($ai_results), $ai_results['request_tokens'] ?? 0, $ai_results['response_tokens'] ?? 0, $execution_time ); // Remove raw response before returning if (isset($ai_results['raw_response'])) { unset($ai_results['raw_response']); } // Ensure the response can be JSON encoded before sending $json_test = json_encode($ai_results); if ($json_test === false) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Cart_Analysis_Handler', 'JSON encoding failed for response', array( 'json_error' => json_last_error(), 'json_error_msg' => json_last_error_msg() )); return new WP_Error('json_encode_error', __('Failed to encode response.', 'ai-product-assistant'), array('status' => 500)); } AIPA_Debug::log(AIPA_Debug::LEVEL_INFO, 'Cart_Analysis_Handler', 'Request completed successfully', array( 'execution_time' => $execution_time, 'has_analysis' => isset($ai_results['analysis']), 'items_analyzed' => count($cart_items) )); return rest_ensure_response($ai_results); } catch (Exception $e) { AIPA_Debug::log(AIPA_Debug::LEVEL_ERROR, 'Cart_Analysis_Handler', 'Exception in handle_cart_analysis_request', array( 'exception' => $e->getMessage(), 'trace' => $e->getTraceAsString() )); return new WP_Error('aipa_service_error', $this->get_ai_service_error_message(), array('status' => 500)); } } /** * Get cart items for AI analysis */ protected function get_cart_items($options) { if (!function_exists('WC') || !isset(WC()->cart)) { return array(); } $cart_items = array(); foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) { $product = $cart_item['data']; $item_data = array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'quantity' => $cart_item['quantity'], 'price' => $product->get_price(), 'total' => $cart_item['line_total'] + $cart_item['line_tax'], 'data' => array() ); // Add product fields based on settings (same as search) $search_fields = isset($options['search_fields']) ? $options['search_fields'] : array(); if (isset($search_fields['title']) && $search_fields['title']) { $item_data['data']['title'] = $product->get_name(); } if (isset($search_fields['short_description']) && $search_fields['short_description']) { $item_data['data']['short_description'] = $product->get_short_description(); } if (isset($search_fields['content']) && $search_fields['content']) { // Limit the length of long description to reduce token consumption $content = $product->get_description(); $item_data['data']['content'] = substr($content, 0, 500); // Limit to 500 characters } if (isset($search_fields['categories']) && $search_fields['categories']) { $categories = array(); $terms = get_the_terms($product->get_id(), 'product_cat'); if ($terms && !is_wp_error($terms)) { foreach ($terms as $term) { $categories[] = $term->name; } } $item_data['data']['categories'] = implode(', ', $categories); } if (isset($search_fields['attributes']) && $search_fields['attributes']) { $attributes = array(); $product_attributes = $product->get_attributes(); if (!empty($product_attributes)) { foreach ($product_attributes as $attribute) { if ($attribute->is_taxonomy()) { $taxonomy = $attribute->get_taxonomy_object(); $attribute_name = $taxonomy->attribute_label; $values = array(); $terms = $attribute->get_terms(); if (!empty($terms)) { foreach ($terms as $term) { $values[] = $term->name; } } $attributes[$attribute_name] = implode(', ', $values); } else { $attribute_name = $attribute->get_name(); $attributes[$attribute_name] = implode(', ', $attribute->get_options()); } } } $item_data['data']['attributes'] = $attributes; } // Add regular_price if selected if (isset($search_fields['regular_price']) && $search_fields['regular_price']) { $item_data['data']['regular_price'] = $product->get_regular_price(); } // Add sale_price if selected if (isset($search_fields['sale_price']) && $search_fields['sale_price']) { $item_data['data']['sale_price'] = $product->get_sale_price(); } // Add tags if selected if (isset($search_fields['tags']) && $search_fields['tags']) { $tags = array(); $terms = get_the_terms($product->get_id(), 'product_tag'); if ($terms && !is_wp_error($terms)) { foreach ($terms as $term) { $tags[] = $term->name; } } $item_data['data']['tags'] = implode(', ', $tags); } // Add brand if selected if (isset($search_fields['brand']) && $search_fields['brand']) { $brands = array(); $terms = get_the_terms($product->get_id(), 'product_brand'); if ($terms && !is_wp_error($terms)) { foreach ($terms as $term) { $brands[] = $term->name; } } $item_data['data']['brand'] = implode(', ', $brands); } // Add weight if selected if (isset($search_fields['weight']) && $search_fields['weight']) { $weight = $product->get_weight(); if ($weight) { $weight_unit = get_option('woocommerce_weight_unit'); $item_data['data']['weight'] = $weight . ' ' . $weight_unit; } } // Add dimensions if selected if (isset($search_fields['dimensions']) && $search_fields['dimensions']) { $dimensions = array(); $length = $product->get_length(); $width = $product->get_width(); $height = $product->get_height(); if ($length || $width || $height) { $dimension_unit = get_option('woocommerce_dimension_unit'); $dimensions_text = ''; if ($length) $dimensions_text .= $length; if ($width) $dimensions_text .= ' x ' . $width; if ($height) $dimensions_text .= ' x ' . $height; $item_data['data']['dimensions'] = trim($dimensions_text, ' x ') . ' ' . $dimension_unit; } } // Add variation data if selected and product is variable if (isset($search_fields['variation_data']) && $search_fields['variation_data'] && $product->is_type('variable')) { if (isset($cart_item['variation']) && !empty($cart_item['variation'])) { $variation_attrs = array(); foreach ($cart_item['variation'] as $attr_slug => $attr_value_slug) { $taxonomy = str_replace('attribute_', '', $attr_slug); $attr_label = $taxonomy; $attr_value = $attr_value_slug; if (taxonomy_exists($taxonomy)) { $term = get_term_by('slug', $attr_value_slug, $taxonomy); if ($term && !is_wp_error($term)) { $attr_label = wc_attribute_label($taxonomy); $attr_value = $term->name; } } else { $attr_label = wc_attribute_label($taxonomy); $attr_value = $attr_value_slug; } $variation_attrs[] = $attr_label . ': ' . $attr_value; } $item_data['data']['variation_data'] = implode(', ', $variation_attrs); } } // Add custom fields if configured if (isset($options['custom_fields']) && is_array($options['custom_fields'])) { $all_meta = get_post_meta($product->get_id()); $custom_meta = array(); foreach ($options['custom_fields'] as $custom_field) { if (isset($custom_field['meta_key'])) { $meta_key = $custom_field['meta_key']; if (isset($all_meta[$meta_key])) { $meta_value = maybe_unserialize($all_meta[$meta_key][0]); if (!empty($meta_value)) { $custom_meta[$meta_key] = $meta_value; } } } } if (!empty($custom_meta)) { $item_data['data']['custom_meta'] = $custom_meta; } } $cart_items[] = $item_data; } return $cart_items; } /** * Call AI API for product search with token usage tracking - MOCK IMPLEMENTATION */ protected function call_ai_api_for_search($query, $products_data, $options) { $api_provider = isset($options['api_provider']) ? $options['api_provider'] : 'unknown'; AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Mock_API', 'Entering call_ai_api_for_search (MOCK)', array( 'query' => substr($query, 0, 100) . '...', 'products_count' => count($products_data), 'configured_provider' => $api_provider, 'options' => array( 'show_explanation' => isset($options['show_explanation']) ? $options['show_explanation'] : true ) )); // MOCK IMPLEMENTATION - Return random product IDs with explanations $product_ids = array(); $explanations = array(); // Check if there are any products if (empty($products_data)) { AIPA_Debug::log(AIPA_Debug::LEVEL_WARNING, 'Mock_API', 'No products data provided', array( 'returning_empty_results' => true )); $result = array( 'product_ids' => array(), 'explanations' => array(), 'request_tokens' => 50, 'response_tokens' => 75, 'raw_response' => json_encode(array( 'mock' => true, 'message' => __('No products found. Please add some products to your WooCommerce store.', 'ai-product-assistant') )) ); AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Mock_API', 'Exiting call_ai_api_for_search (MOCK - empty)', array( 'configured_provider' => $api_provider, 'reason' => 'no_products_data' )); return $result; } // Randomly select 1-5 products from the batch $available_count = count($products_data); $selected_count = min($available_count, wp_rand(1, min(5, $available_count))); $shuffled = $products_data; shuffle($shuffled); for ($i = 0; $i < $selected_count; $i++) { $product_id = $shuffled[$i]['id']; $product_ids[] = $product_id; $explanations[$product_id] = sprintf( /* translators: %s: search query */ __('This product matches your search for "%s". Upgrade to Pro for real AI-powered search results.', 'ai-product-assistant'), $query ); } $result = array( 'product_ids' => $product_ids, 'explanations' => $explanations, 'request_tokens' => 50, 'response_tokens' => 75, 'raw_response' => json_encode(array( 'mock' => true, 'message' => __('This is a mock search result. Upgrade to Pro for AI-powered search.', 'ai-product-assistant') )) ); AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Mock_API', 'Exiting call_ai_api_for_search (MOCK)', array( 'product_ids_count' => count($product_ids), 'selected_products' => $product_ids, 'has_explanations' => !empty($explanations), 'configured_provider' => $api_provider, 'tokens' => array( 'request' => 50, 'response' => 75 ) )); return $result; } /** * Call AI API for cart analysis - MOCK IMPLEMENTATION */ protected function call_ai_api_for_cart_analysis($query, $cart_items, $options) { $api_provider = isset($options['api_provider']) ? $options['api_provider'] : 'unknown'; AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Mock_API', 'Entering call_ai_api_for_cart_analysis (MOCK)', array( 'query' => substr($query, 0, 100) . '...', 'cart_items_count' => count($cart_items), 'cart_total_items' => array_sum(array_column($cart_items, 'quantity')), 'configured_provider' => $api_provider )); // MOCK IMPLEMENTATION - Return mock analysis $mock_analysis = sprintf( /* translators: %s: user's goal or query */ __('This is a mock analysis of your cart items for the goal: "%s". In the Pro version, AI would analyze compatibility and completeness of your cart items.', 'ai-product-assistant'), $query ); $result = array( 'analysis' => $mock_analysis, 'is_complete' => true, 'missing_items' => array( __('Mock missing item 1', 'ai-product-assistant'), __('Mock missing item 2', 'ai-product-assistant'), ), 'compatibility_issues' => array(), 'recommendations' => array( __('Mock recommendation: Consider adding complementary products', 'ai-product-assistant'), __('Mock recommendation: Your cart looks great!', 'ai-product-assistant'), ), 'request_tokens' => 75, 'response_tokens' => 100, 'raw_response' => json_encode(array( 'mock' => true, 'message' => __('This is a mock cart analysis. Upgrade to Pro for real AI analysis.', 'ai-product-assistant') )) ); AIPA_Debug::log(AIPA_Debug::LEVEL_DEBUG, 'Mock_API', 'Exiting call_ai_api_for_cart_analysis (MOCK)', array( 'analysis_length' => strlen($mock_analysis), 'is_complete' => true, 'missing_items_count' => 2, 'compatibility_issues_count' => 0, 'recommendations_count' => 2, 'configured_provider' => $api_provider, 'tokens' => array( 'request' => 75, 'response' => 100 ) )); return $result; } }