namespace, '/chat', array( 'methods' => 'POST', 'callback' => array( $this, 'handle_chat' ), 'permission_callback' => array( $this, 'check_chat_permission' ), 'args' => array( 'message' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'session_id' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'required' => false, 'type' => 'object', ), ), ) ); // Product search endpoint register_rest_route( $this->namespace, '/products/search', array( 'methods' => 'GET', 'callback' => array( $this, 'search_products' ), 'permission_callback' => array( $this, 'check_chat_permission' ), 'args' => array( 'query' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'limit' => array( 'required' => false, 'type' => 'integer', 'default' => 5, ), ), ) ); // Cart endpoints register_rest_route( $this->namespace, '/cart/add', array( 'methods' => 'POST', 'callback' => array( $this, 'add_to_cart' ), 'permission_callback' => array( $this, 'check_cart_permission' ), 'args' => array( 'product_id' => array( 'required' => true, 'type' => 'integer', ), 'quantity' => array( 'required' => false, 'type' => 'integer', 'default' => 1, ), 'variation_id' => array( 'required' => false, 'type' => 'integer', ), ), ) ); register_rest_route( $this->namespace, '/cart/get', array( 'methods' => 'GET', 'callback' => array( $this, 'get_cart' ), 'permission_callback' => array( $this, 'check_cart_permission' ), ) ); register_rest_route( $this->namespace, '/cart/remove', array( 'methods' => 'POST', 'callback' => array( $this, 'remove_from_cart' ), 'permission_callback' => array( $this, 'check_cart_permission' ), 'args' => array( 'cart_item_key' => array( 'required' => true, 'type' => 'string', ), ), ) ); // Settings endpoint (for widget configuration) register_rest_route( $this->namespace, '/settings', array( 'methods' => 'GET', 'callback' => array( $this, 'get_settings' ), 'permission_callback' => array( $this, 'check_public_read_permission' ), ) ); // NEW: User authentication check endpoint register_rest_route( $this->namespace, '/user/check', array( 'methods' => 'GET', 'callback' => array( $this, 'check_user_auth' ), 'permission_callback' => array( $this, 'check_public_read_permission' ), ) ); } /** * Check chat permission * * @param WP_REST_Request $request Request object * @return bool */ public function check_chat_permission( $request ) { // Check rate limiting if ( !$this->check_rate_limit( $request ) ) { return new WP_Error( 'rate_limit_exceeded', __( 'Too many requests. Please try again later.', 'aica-smart-ai-commerce-assistant' ), array( 'status' => 429 ) ); } return true; } /** * Check cart permission * * @return bool */ public function check_cart_permission() { // Anyone can access cart endpoints return true; } /** * Check rate limit * * @param WP_REST_Request $request Request object * @return bool */ private function check_rate_limit( $request ) { $settings = get_option( 'aica_settings', array() ); if ( !isset( $settings['rate_limit_enabled'] ) || $settings['rate_limit_enabled'] !== 'yes' ) { return true; } $session_id = $request->get_param( 'session_id' ); $transient_key = 'aica_rate_limit_' . md5( $session_id ); $count = get_transient( $transient_key ); if ( $count === false ) { set_transient( $transient_key, 1, HOUR_IN_SECONDS ); return true; } $limit = isset( $settings['rate_limit_messages'] ) ? intval( $settings['rate_limit_messages'] ) : 50; if ( $count >= $limit ) { return false; } set_transient( $transient_key, $count + 1, HOUR_IN_SECONDS ); return true; } /** * Handle chat request * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error */ public function handle_chat( $request ) { $message = sanitize_text_field( $request->get_param( 'message' ) ); $session_id = sanitize_text_field( $request->get_param( 'session_id' ) ); $context = $request->get_param( 'context' ); // Context is an array/object, validated later // Save user message $this->save_message( $session_id, $message, 'user', $context ); // Log analytics event $this->log_analytics( $session_id, 'message_sent', array( 'message_length' => strlen( $message ), ) ); try { // NEW: Check if this is an order tracking request if ( $this->is_order_tracking_request( $message ) ) { return $this->handle_order_tracking_request( $message, $session_id ); } // Use AI Engine for intelligent response $ai_engine = AICA_AI_Engine::get_instance(); $ai_response = $ai_engine->generate_response( $message, $session_id, $context ); // Handle cart actions if present if ( isset( $ai_response['actions'] ) ) { foreach ( $ai_response['actions'] as $action ) { if ( $action['type'] === 'add_to_cart' && isset( $action['product_id'] ) ) { $cart_manager = AICA_Cart_Manager::get_instance(); $cart_result = $cart_manager->add_to_cart( $action['product_id'] ); if ( $cart_result['success'] ) { $ai_response['cart'] = $cart_result['cart']; } } } } // Save assistant response $this->save_message( $session_id, $ai_response['message'], 'assistant', array( 'products' => $ai_response['products'] ?? null, 'actions' => $ai_response['actions'] ?? null, ) ); // Log product recommendations if ( !empty( $ai_response['products'] ) ) { $this->log_analytics( $session_id, 'products_recommended', array( 'count' => count( $ai_response['products'] ), 'product_ids' => array_column( $ai_response['products'], 'id' ), ) ); } return new WP_REST_Response( array( 'success' => true, 'message' => $ai_response['message'], 'products' => $ai_response['products'] ?? array(), 'actions' => $ai_response['actions'] ?? array(), 'cart' => $ai_response['cart'] ?? null, 'session_id' => $session_id, ), 200 ); } catch ( Exception $e ) { error_log( 'AICA Chat Error: ' . $e->getMessage() ); return new WP_Error( 'ai_error', __( 'Sorry, I encountered an error. Please try again.', 'aica-smart-ai-commerce-assistant' ), array( 'status' => 500 ) ); } } /** * NEW: Check if message is about order tracking * * @param string $message User message * @return bool */ private function is_order_tracking_request( $message ) { $tracking_keywords = array( // English 'track', 'order', 'tracking', 'where', 'status', 'delivery', 'shipping', 'package', 'arrived', 'when will', 'when does', // Italian 'traccia', 'ordine', 'tracciamento', 'dov', 'dove', 'stato', 'consegna', 'spedizione', 'pacco', 'arrivato', 'quando arriva', 'quando sarΓ ', 'informazioni ordine', 'dettagli ordine', 'mio ordine', ); $message_lower = strtolower( $message ); foreach ( $tracking_keywords as $keyword ) { if ( strpos( $message_lower, $keyword ) !== false ) { return true; } } return false; } /** * NEW: Handle order tracking request * * @param string $message User message * @param string $session_id Session ID * @return WP_REST_Response */ private function handle_order_tracking_request( $message, $session_id ) { $user_id = get_current_user_id(); // Check if user is logged in if ( !$user_id ) { $this->save_message( $session_id, 'Order tracking requested (user not logged in)', 'assistant', array( 'require_login' => true, ) ); $this->log_analytics( $session_id, 'order_tracking_request', array( 'authenticated' => false, ) ); return new WP_REST_Response( array( 'success' => true, 'message' => __( 'Per tracciare i tuoi ordini, per favore effettua il login al tuo account.', 'aica-smart-ai-commerce-assistant' ), 'require_login' => true, ), 200 ); } // Get user orders $orders = wc_get_orders( array( 'customer_id' => $user_id, 'limit' => 5, 'orderby' => 'date', 'order' => 'DESC', ) ); if ( empty( $orders ) ) { $this->save_message( $session_id, 'Order tracking requested (no orders found)', 'assistant' ); return new WP_REST_Response( array( 'success' => true, 'message' => __( 'Non hai ordini recenti da tracciare.', 'aica-smart-ai-commerce-assistant' ), ), 200 ); } // Format orders for display $formatted_orders = array(); $message_text = __( 'Ecco i tuoi ordini recenti:', 'aica-smart-ai-commerce-assistant' ) . "\n\n"; foreach ( $orders as $order ) { $order_id = $order->get_id(); $order_number = $order->get_order_number(); $status = $order->get_status(); $status_name = $this->translate_order_status( $status ); $date = $order->get_date_created()->date_i18n( get_option( 'date_format' ) ); $total = $order->get_formatted_order_total(); // Get tracking info from order meta $tracking_number = $order->get_meta( '_tracking_number' ); $tracking_url = $order->get_meta( '_tracking_url' ); $formatted_orders[] = array( 'id' => $order_id, 'number' => $order_number, 'status' => $status, 'status_name' => $status_name, 'date' => $date, 'total' => $total, 'tracking_number' => $tracking_number ?: null, 'tracking_url' => $tracking_url ?: null, 'view_url' => $order->get_view_order_url(), ); // Build message in Italian $message_text .= sprintf( "**Ordine #%s** - %s\n" . "πŸ“… Data: %s\n" . "πŸ’° Totale: %s\n" . "πŸ“¦ Stato: %s\n", $order_number, $status_name, $date, $total, $status_name ); if ( $tracking_number ) { $message_text .= sprintf( "πŸ” Tracciamento: %s\n", $tracking_number ); } $message_text .= "\n"; } // Save assistant response $this->save_message( $session_id, $message_text, 'assistant', array( 'orders' => $formatted_orders, ) ); // Log event $this->log_analytics( $session_id, 'order_tracking_viewed', array( 'order_count' => count( $orders ), 'user_id' => $user_id, ) ); return new WP_REST_Response( array( 'success' => true, 'message' => $message_text, 'orders' => $formatted_orders, ), 200 ); } /** * NEW: Translate WooCommerce order status to Italian * * @param string $status Order status * @return string Translated status */ private function translate_order_status( $status ) { $status_translations = array( 'pending' => __( 'In Sospeso', 'aica-smart-ai-commerce-assistant' ), 'processing' => __( 'In Elaborazione', 'aica-smart-ai-commerce-assistant' ), 'on-hold' => __( 'In Attesa', 'aica-smart-ai-commerce-assistant' ), 'completed' => __( 'Completato', 'aica-smart-ai-commerce-assistant' ), 'cancelled' => __( 'Cancellato', 'aica-smart-ai-commerce-assistant' ), 'refunded' => __( 'Rimborsato', 'aica-smart-ai-commerce-assistant' ), 'failed' => __( 'Non Riuscito', 'aica-smart-ai-commerce-assistant' ), ); return isset( $status_translations[ $status ] ) ? $status_translations[ $status ] : wc_get_order_status_name( $status ); } /** * NEW: Check user authentication status * * @param WP_REST_Request $request Request object * @return WP_REST_Response */ public function check_user_auth( $request ) { return new WP_REST_Response( array( 'is_authenticated' => is_user_logged_in(), 'user_id' => get_current_user_id(), 'user_email' => is_user_logged_in() ? wp_get_current_user()->user_email : null, ), 200 ); } /** * Get AI response (placeholder for now) * * @param string $message User message * @param string $session_id Session ID * @param array $context Context data * @return array */ private function get_ai_response( $message, $session_id, $context ) { // For now, return a simple response // We'll implement the real AI engine in Phase 4 $message_lower = strtolower( $message ); // Simple keyword detection if ( strpos( $message_lower, 'product' ) !== false || strpos( $message_lower, 'show' ) !== false ) { // Search for products $products = $this->simple_product_search( $message ); return array( 'message' => sprintf( /* translators: %d: number of products */ __( 'I found %d products that might interest you:', 'aica-smart-ai-commerce-assistant' ), count( $products ) ), 'products' => $products, ); } if ( strpos( $message_lower, 'cart' ) !== false ) { return array( 'message' => __( 'Let me check your cart for you...', 'aica-smart-ai-commerce-assistant' ), 'actions' => array( 'show_cart' ), ); } // Default response return array( 'message' => __( 'Hi! I\'m your AI shopping assistant. I can help you find products, answer questions, and manage your cart. What would you like to know?', 'aica-smart-ai-commerce-assistant' ), ); } /** * Simple product search (temporary implementation) * * @param string $query Search query * @return array */ private function simple_product_search( $query ) { $args = array( 'post_type' => 'product', 'posts_per_page' => 5, 'post_status' => 'publish', 's' => $query, ); $products = wc_get_products( $args ); $result = array(); foreach ( $products as $product ) { $result[] = array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'price' => $product->get_price(), 'regular_price' => $product->get_regular_price(), 'sale_price' => $product->get_sale_price(), 'image' => wp_get_attachment_url( $product->get_image_id() ), 'url' => $product->get_permalink(), 'in_stock' => $product->is_in_stock(), 'stock_quantity' => $product->get_stock_quantity(), ); } return $result; } /** * Search products endpoint * * @param WP_REST_Request $request Request object * @return WP_REST_Response */ public function search_products( $request ) { $query = sanitize_text_field( $request->get_param( 'query' ) ); $limit = intval( $request->get_param( 'limit' ) ); if ( $limit <= 0 ) { $limit = 5; } // Use AI Engine's RAG search $ai_engine = AICA_AI_Engine::get_instance(); $ai_engine_reflection = new ReflectionClass( $ai_engine ); $method = $ai_engine_reflection->getMethod( 'retrieve_relevant_products' ); $method->setAccessible( true ); $products = $method->invoke( $ai_engine, $query ); return new WP_REST_Response( array( 'success' => true, 'products' => array_slice( $products, 0, $limit ), ), 200 ); } /** * Add product to cart * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error */ public function add_to_cart( $request ) { $product_id = intval( $request->get_param( 'product_id' ) ); $quantity = intval( $request->get_param( 'quantity' ) ); $variation_id = !empty( $request->get_param( 'variation_id' ) ) ? intval( $request->get_param( 'variation_id' ) ) : 0; $cart_manager = AICA_Cart_Manager::get_instance(); $result = $cart_manager->add_to_cart( $product_id, $quantity, $variation_id ); if ( $result['success'] ) { return new WP_REST_Response( $result, 200 ); } return new WP_Error( 'add_to_cart_failed', $result['message'], array( 'status' => 400 ) ); } /** * Get cart contents * * @return WP_REST_Response */ public function get_cart() { $cart_manager = AICA_Cart_Manager::get_instance(); $cart = $cart_manager->get_cart_data(); return new WP_REST_Response( array( 'success' => true, 'cart' => $cart, ), 200 ); } /** * Remove item from cart * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error */ public function remove_from_cart( $request ) { $cart_item_key = sanitize_text_field( $request->get_param( 'cart_item_key' ) ); $cart_manager = AICA_Cart_Manager::get_instance(); $result = $cart_manager->remove_from_cart( $cart_item_key ); if ( $result['success'] ) { return new WP_REST_Response( $result, 200 ); } return new WP_Error( 'remove_failed', $result['message'], array( 'status' => 400 ) ); } /** * Format cart data * * @return array */ private function format_cart() { $cart_items = array(); foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { $product = $cart_item['data']; $cart_items[] = array( 'key' => $cart_item_key, 'product_id' => $cart_item['product_id'], 'name' => $product->get_name(), 'quantity' => $cart_item['quantity'], 'price' => $product->get_price(), 'subtotal' => $cart_item['line_subtotal'], 'image' => wp_get_attachment_url( $product->get_image_id() ), ); } return array( 'items' => $cart_items, 'item_count' => WC()->cart->get_cart_contents_count(), 'subtotal' => WC()->cart->get_cart_subtotal(), 'total' => WC()->cart->get_cart_total(), ); } /** * Get widget settings * * @return WP_REST_Response */ public function get_settings() { $settings = get_option( 'aica_settings', array() ); $magic_prompts = get_option( 'aica_magic_prompts', array() ); return new WP_REST_Response( array( 'success' => true, 'settings' => array( 'widget_position' => $settings['widget_position'] ?? 'bottom-right', 'primary_color' => $settings['primary_color'] ?? '#0066FF', 'widget_title' => $settings['widget_title'] ?? __( 'AI Assistant', 'aica-smart-ai-commerce-assistant' ), 'greeting_message' => $settings['greeting_message'] ?? __( 'Hi! How can I help you today?', 'aica-smart-ai-commerce-assistant' ), 'magic_prompts' => $magic_prompts, ), ), 200 ); } /** * Save conversation message * * @param string $session_id Session ID * @param string $message Message content * @param string $role Role (user or assistant) * @param array $metadata Additional metadata * @return void */ private function save_message( $session_id, $message, $role, $metadata = null ) { global $wpdb; $table = $wpdb->prefix . 'aica_conversations'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->insert( $table, array( 'session_id' => $session_id, 'user_id' => get_current_user_id(), 'message' => $message, 'role' => $role, 'metadata' => $metadata ? wp_json_encode( $metadata ) : null, 'created_at' => current_time( 'mysql' ), ), array( '%s', '%d', '%s', '%s', '%s', '%s' ) ); } /** * Log analytics event * * @param string $session_id Session ID * @param string $event_type Event type * @param array $event_data Event data * @return void */ private function log_analytics( $session_id, $event_type, $event_data = null ) { global $wpdb; $table = $wpdb->prefix . 'aica_analytics'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->insert( $table, array( 'session_id' => $session_id, 'event_type' => $event_type, 'event_data' => $event_data ? wp_json_encode( $event_data ) : null, 'created_at' => current_time( 'mysql' ), ), array( '%s', '%s', '%s', '%s' ) ); } /** * Check public read permission * * @return bool */ public function check_public_read_permission() { // These endpoints are designed to be public to support the frontend widget. // /settings: Publicly exposes only non-sensitive configuration needed for widget initialization. // /user/check: Checks strict authentication status without exposing user data. return true; } }